Найменше лексикографічне обертання рядка з використанням суфіксних масивів в O (n)


9

Я цитую проблему з ACM 2003:

Розглянемо рядок довжиною n (1 <= n <= 100000). Визначте його мінімальне лексикографічне обертання. Наприклад, обертання рядка "alabala" є:

алабала

лабалаа

врізний

балаала

алаалаб

laalaba

аалабал

і найменший серед них "аалабал".

Щодо рішення - я знаю, що мені потрібно побудувати масив суфіксів - і скажімо, що я можу це зробити в O (n). Все ще моє запитання: як я можу знайти найменше обертання в O (n)? (n = довжина рядка)

Мене дуже цікавить ця проблема, і досі я якось не отримую рішення. Мене більше цікавить концепція і як вирішити проблему, а не конкретна реалізація.

Примітка: мінімальний обертання означає в тому ж порядку, що і в англійському словнику - "dwor" є перед "словом", оскільки d - перед w.

EDIT: побудова суфіксного масиву займає O (N)

ОСТАНА РЕДАКТ: Я думаю, що знайшов рішення !!! Що робити, якщо я просто об'єднав дві струни? Отже, якщо рядок є "alabala", новий рядок міг би я "alabalaalabala", і тепер я просто побудую суфіксний масив цього (в O (2n) = O (n)) і отримаю перший суфікс? Я думаю, це може бути правильним. Як ти гадаєш? Дякую!


Як ви визначаєте "мінімум"? Що використовується метрика (можливо, це очевидно, але я не експерт)?
Джорджіо

Дякую за замітку! Я вважав, що обертання повинно бути мінімальним (мінімальне зміщення), а не результатом лексикографічного порядку обертання wrt.
Джорджіо

Мені все-таки щось не вистачає: чи входить складність масиву суфіксів у складність? Я думаю, що для побудови масиву та сортування масиву потрібно більше, ніж O (n) .
Джорджіо

Думаю, ідея повторити початковий рядок двічі чудова! Тоді ви можете побудувати масив суфіксів у O (2n) = O (n). Але вам не потрібно сортувати це, щоб знайти мінімум? Для цього потрібно більше, ніж O (n), правда?
Джорджо

@Giorgio добре, що сам масив суфіксів містить уже відсортовані достатки . І ще одна примітка, можливо трохи офтопічна - не забувайте, що сортування можна проводити навіть у o (n) з деякими припущеннями щодо відсортованих об’єктів (ознайомтеся, наприклад, із сортуванням radix, наприклад)
Tomy

Відповіді:


5

Простий трюк, щоб побудувати всі обертання рядка довжиною N - це об'єднати рядок із собою.

Тоді кожна підрядка N-довжини цього рядка довжиною 2N - це обертання вихідної рядки.

Визначення "лексикографічно мінімальної" підрядки виконується за допомогою вашої конструкції дерева O (N).


0

Я майже впевнений, що інформація, що міститься в суфіксному масиві, недостатня, щоб допомогти вам дістатися до O (n), але, принаймні, може допомогти вам до O (n log n). Розглянемо це сімейство суфіксів:

a
aba
abacaba
abacabadabacaba
abacabadabacabaeabacabadabacaba
...

Наступний суфікс ви будуєте, беручи попередній суфікс (скажімо aba), додаючи наступний символ, який ще не використовується, а потім знову додаєте попередній суфікс (так aba -> aba c aba).

Тепер розглянемо ці рядки (пробіл додано для наголосу, але не є частиною рядка):

ad abacaba
bd abacaba
cd abacaba

Для цих трьох рядків початок масиву суфіксів виглядатиме так:

a
aba
abacaba
(other suffixes)

Виглядає знайомо? Звичайно, ці рядки призначені для створення цього масиву суфіксів. Тепер, залежно від початкової літери (a, b або c), "правильний" індекс (рішення вашої проблеми) - це перший, другий або третій суфікс у списку вище.

Вибір першої літери майже не впливає на суфіксний масив; зокрема, це не впливає на порядок перших трьох суфіксів у масиві суфіксів. Це означає, що у нас є n n рядків, для яких суфіксний масив надзвичайно схожий, але індекс 'правильного' сильно відрізняється.

Хоча я не маю жодних вагомих доказів, це напевно підказує мені, що у вас немає іншого вибору, як порівняти обертання, відповідні цим першим трьом індексам у масиві, за їх лексикографічним упорядкуванням, що, в свою чергу, означає, що вам знадобиться принаймні O (n log n) час для цього (оскільки кількість альтернативних перших символів - у нашому випадку 3 - це log n, а порівняння двох рядків займає час O (n)).

Це не виключає можливості алгоритму O (n). Я просто сумніваюся, що суфіксний масив допомагає вам досягти цього часу.


0

Найменше обертання - це те, що починається з деяких суфіксів із суфіксного масиву. Суфікси лексикографічно впорядковані. Це дає вам великий стрибок:

  • ви знаєте, що як тільки ви отримаєте такий k, що обертання, починаючи з суфікса k , менше, ніж обертання, починаючи з суфікса k +1, ви закінчите (починаючи з першого);
  • можна порівняти "обертання, починаючи з суфікса k менше, ніж обертання, починаючи з суфікса k +1" в O (1), порівнюючи довжини суфіксів і необов'язково, порівнюючи один символ з іншим символом.

EDIT: "один символ з іншим символом" може бути не завжди таким, це може бути більше одного символу, але в цілому ви не вивчаєте більше ніж n символів протягом усього процесу пошуку, тому це O (n).

Короткий доказ: Ви вивчаєте символи лише тоді, коли суфікс k +1 довший за суфікс k , і ви зупиняєтесь і знайшли своє рішення, якщо суфікс k +1 коротший за суфікс k (тоді ви знаєте, що суфікс k - той, який ви шукали). Таким чином, ви вивчаєте символи лише тоді, коли перебуваєте у зростаючій (по довжині) послідовності суфіксів. Оскільки ви вивчаєте лише зайві символи, ви не можете вивчити більше n символів.

EDIT2: Цей алгоритм спирається на те, що "якщо в суфіксному масиві є два сусідні суфікси, а попередній коротший за наступний, попередній - префікс наступного". Якщо це неправда, то вибачте.

EDIT3: Ні, це не відповідає. "абааа" має таблиці суфіксів "а", "аа", "ааа", "абааа", "бааа". Але, можливо, ця думка в кінцевому підсумку може призвести до рішення, просто ще деякі деталі повинні бути вдосконалені ще більше. Першочергове питання полягає в тому, чи можна якось зробити вищезгадане порівняння, проаналізувавши меншу кількість символів, тож я (O) п, що я вважаю, можливо. Я просто не можу сказати, як зараз.


0

Проблема:

Лексикографічно найменш кругла підрядок - це проблема знаходження обертання рядка, що має найнижчий лексикографічний порядок усіх таких обертів. Наприклад, лексикографічно мінімальне обертання "bbaaccaadd" було б "aaccaaddbb".

Рішення:

Алгоритм часу AO (n) був запропонований Жаном П'єром Дювалем (1983).

Враховуючи два індекси iі j, алгоритм Дюваля порівнює рядкові відрізки довжини, j - iпочинаючи з iі j(називається "дуель" ). Якщо index + j - iбільша довжина струни, відрізок формується обертанням навколо.

Наприклад, розглянемо s = "baabbaba", i = 5 і j = 7. Оскільки j - i = 2, перший відрізок, що починається з i = 5, є "ab". Другий відрізок, що починається від j = 7, побудований обертанням навколо, а також є "ab". Якщо рядки лексикографічно рівні, як у наведеному вище прикладі, ми вибираємо той, що починається з i як переможець, а це = 5.

Описаний вище процес повторювався, поки ми не отримаємо єдиного переможця. Якщо вхідний рядок має непарну довжину, останній символ виграє без порівняння в першій ітерації.

Часова складність:

Перша ітерація порівнює n рядків, довжиною 1 (n / 2 порівняння), друга ітерація може порівнювати n / 2 рядка довжиною 2 (n / 2 порівняння) тощо, поки i-та ітерація не порівнює 2 рядки довжина n / 2 (n / 2 порівняння). Оскільки кількість переможців щоразу зменшується вдвічі, то висота дерева рекурсії - log (n), що дає нам алгоритм O (n log (n)). Для малих n це приблизно O (n).

Космічна складність теж є O (n), оскільки в першій ітерації ми повинні зберігати n / 2 переможців, у другій ітерації n / 4 переможців тощо. (Вікіпедія стверджує, що цей алгоритм використовує постійний простір, я не розумію, як).

Ось реалізація Scala; не соромтеся перетворюватися на улюблену мову програмування.

def lexicographicallyMinRotation(s: String): String = {
 @tailrec
 def duel(winners: Seq[Int]): String = {
   if (winners.size == 1) s"${s.slice(winners.head, s.length)}${s.take(winners.head)}"
   else {
     val newWinners: Seq[Int] = winners
       .sliding(2, 2)
       .map {
         case Seq(x, y) =>
           val range = y - x
           Seq(x, y)
             .map { i =>
               val segment = if (s.isDefinedAt(i + range - 1)) s.slice(i, i + range)
               else s"${s.slice(i, s.length)}${s.take(s.length - i)}"
               (i, segment)
             }
             .reduce((a, b) => if (a._2 <= b._2) a else b)
             ._1
         case xs => xs.head
       }
       .toSeq
     duel(newWinners)
   }
 }

 duel(s.indices)
}

-1

Я не бачу нічого кращого за O (N²).

Якщо у вас є список з N цілих чисел, ви можете вибрати найменше в порівнянні O (N).

Тут ви маєте список з N рядків розміром N (побудова їх нічого не коштує; рядок повністю визначається її початковим індексом). Ви можете вибрати найменше в порівнянні O (N). Але кожне порівняння - це O (N) основних операцій. Отже, складність дорівнює O (N²).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.