Блог пользователя Gerald

Автор Gerald, 14 лет назад, По-русски
На одной из тренировок школьников на NEERC в этом году, была одна интересная строковая задача. Вот её приблизительное условие:
Строка называется хорошей, если какой либо её префикс ненулевой длины и отличный от всей строки совпадает с её суффиксом. Дана строка длиной до 105 символов, нужно для каждого её циклического сдвига определить является он хорошим или нет.

Условие в оригинале и авторское решение по задаче можно найти где-то-тут. Оригинальное название задачи "Заклинание Границы".

В авторском решении, что-то весьма хитрое с z-функцией и разделяй-и-властвуй, кажется, я не очень понял. Предлагаю пообсуждать решение здесь :). 


P.S. Некто e-maxx, говорил, что, вроде бы, подобная задача была когда то в Петрозаводске на Станкевич контесте. И что у нее есть решение как z-функцией, так и суффиксным деревом. 
  • Проголосовать: нравится
  • +6
  • Проголосовать: не нравится

14 лет назад, # |
  Проголосовать: нравится -8 Проголосовать: не нравится

Для каждого сдвига перебираем длину суфикса, который совпадает с префиксом, бинарным поиском. Суфикс и префикс сравниваем за константу полиномиальными хешами.

Если не проходит по времени, увеличиваем таймлимит :о)

  • 14 лет назад, # ^ |
      Проголосовать: нравится +10 Проголосовать: не нравится
    Бинарным поиском длину суффикса? o_O А оно... монотонное?
  • 14 лет назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится
    Дело в том, что эта функция не монотонная. Бинпоиск делать нельзя.
    Например для строки "abaсaba", "aba" совпадает, а "ab" нет.
14 лет назад, # |
Rev. 5   Проголосовать: нравится +3 Проголосовать: не нравится
UPD: это неверно. См. далее верное решение.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Для каждого суффикса t длины большей n (он отвечает какому-то циклическому сдвигу s) определим, какая вершина дерева соответствует суффиксу tR (то бишь префиксу t) такому, что их пересечение образует в точности циклический сдвиг - т.е. искомый префикс t кончается в позиции, отстоящей на n вперёд от начала рассматриваемого суффикса t. Вроде бы это можно сделать за O(N).
    Я что-то не понимаю, как это за O(N) сделать.
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Ну как же. Если у нас есть суффикс t[i..], то мы знаем, в каком символе должен начинаться парный ему — это tR[(n - i)..], ибо циклический сдвиг — это t[i..(i + n - 1)]. В процессе построения дерева tR, как только мы достроили ветку для этого парного суффикса n - i, надо просто в каком-то заведенном массивчике для i-го суффикса t прописать ссылку на эту вершину дерева.
  • 14 лет назад, # ^ |
    Rev. 3   Проголосовать: нравится +8 Проголосовать: не нравится
    А в tR же суффикс будет отвечать не префиксу исходной строки, а перевернутому префиксу. Т.е. к примеру строка abab - не будет хорошей, т.к. ab != ba, а она хорошая.

    Или я не правильно понял идею?
    • 14 лет назад, # ^ |
        Проголосовать: нравится +10 Проголосовать: не нравится
      Да, вы правы.

      Я сейчас повнимательней полистал Гасфилда, и родилась такая идея: строим дерево для строки t = s + s + s.
      Теперь такое наблюдение: если есть какая-то подстрока α, повторяющаяся два раза подряд в средней части t, т.е. t = wααu, n ≤ |wα| < 2n, то циклический сдвиг, равный суффиксу wα длины n, является хорошим. Гасфилд называет такие места тандемными повторами.

      Все тандемные повторы можно найти за время , где k — количество этих повторов. Так как k = O(n), то итоговое время . Описан алгоритм здесь, выглядит вроде не очень сложно.
      • 14 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Ставлю плюс =))
        Отличное решение =)
      • 14 лет назад, # ^ |
          Проголосовать: нравится +5 Проголосовать: не нравится
        Если кто хочет попрактиковаться в подсчете тандемных повторов, то можно попробовать эту задачу: http://www.spoj.pl/problems/KPARCH/.
        Там, кстати, можно обойтись без суффиксных массивов, только "разделяй и влавствуй" + z-функция.
      • 14 лет назад, # ^ |
          Проголосовать: нравится +10 Проголосовать: не нравится
        Почему тандемных повторов O(n)? Если я правильно все понял,  для s=a^n их будет порядка n^2
14 лет назад, # |
Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

Думается всетаки можно таким методом решить.

Научимся решать: http://olympiads.ru/zaoch/2009/problems/k.shtml .

Я в свое время решал с помощью KMP. Собственно, текст в котором ищем text = s + s, искомый образец patern = s. n = strlen(s). Как обычно построим префикс функцию и начнем искать образец в тексте. Начиная с символа n-1 (индексация с 0) при поиске образца в тексте обращаем внимание на следующий факт.

Если после некоторой итерации указатель на символ в образце равен n, то в случае с задачей поиска строки в подстроке можно говорить о том, что найдено вхождение образца в текст. В нашей же задаче значение указателя -- это и будет наибольший префикс, который является суффиксом. Если p = n, то возьмем значение префикс функции от s.

  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Понимаете так делать нельзя. Так вы найдете наибольший суффикс который равен префиксу нулевого циклического сдвига строки. То есть у вас суффикс меняется всё время, а префикс нет.

    P.S. Задача уже решена, сверху Skiminok написал отличное решение с помощью нахождение тандемных повторов.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Эта задача решается суфф. массивом. + rmq + dsu. придумал еще на контесте, но не успел написать.

Посмотрим на циклический сдвиг i. Когда он хороший? когда  .  Отлично. Построим суффмассив по циклическим сдвигам строки s. Посчитаем lcp.

Проверим условие для всех j, расположенных в суфмассиве раньше чем i. Симметрично сделаем для остальных.

Будем идти по массиву с начала в конец. На i-ом шаге ответим для i-го лексикографически цикл. сдвига. Очевидно что проверка - запрос к дереву отрезков для написанной выше функции. Осталось эту функцию пересчитать. Утверждается что в любой момент все строки можно разбить на множества с одинаковым lcp, причем эти множества - отрезки в суффмассиве. Честно объедением отрезки которые теперь имеют одинаковое lcp. Не сложно что это несколько отрезков ближних к текущей позиции. Причем таких объединений всего O(N). Кадое делается деревом отрезков за log. Чтобы просто понимать на каких отрезках делать объединение поможет dsu. Думаю можно и без него. Итого суммарно O(NlogN).

Думаю что написал довольно криво, вопросы приветствуются.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Вообще неравенство какое-то странное, оно по модулю что ли? или ты строку саму к себе приписываешь?
    • 14 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится
      Неравенство нормальное. По модулю там ничего нет. lcp считается в зацикленной строке.