Только что я сидел разбирался с суффиксным автоматом (вроде бы все понятно и пишу норм, но если долго не писать забывается) и в примере задач увидел задачу из заголовка.
Ссылка на статью на e-maxx'e, там в примере задач она последняя.
Ну в общем убил я где-то минут 30-40 на то чтоб понять это, но толку нет. Не все так просто как казалось. Увы...
Но буквально пару минут я вспомнил о боре, и подумал: "а чего я хочу этого именно от суффиксного автомата, если это можно сделать в разы проще бором?"
Моя идея в том чтобы просто забросить все строчки в бор, а потом обходом в глубину (хотя зачем?) просто переходить по состояниям пока есть переход только по одной букве, иначе понятно, что уже общего префикса не будет. Идея проще простого, да и написать это очень легко.
Вот код.
Upd. Как же я люблю тупить... На e-maxx'e описано решение задачи о нахождении наибольшей общей подстроки. А префикс можно искать намного проще даже втупую, хотя втупую придется каждый раз пробегать за количество строк, чтобы сравнить равны ли соответствующие символы. Но все же этот алгоритм вполне можно использовать.
Извиняюсь за тупку, не судите строго.
Upd2. Да и идея наверное далеко не новая.
===========================================
И пользуясь моментом пару вопросов о автомате:
-Где можно было бы почитать о наибольшей общей подстроке нескольких строк, чтобы точно понять это?
-И раз уж пошел разговор о автомате, то можно ли, и как, сделать его персистентным? Т.е. добавлять символы очень удобно, но хотелось бы и уметь удалять их, хотя бы для начала по одному.
===========================================
Ура, я таки нашел в чем польза от этого способа. Немного изменив код, я пришел к онлайновости этого алгоритма. Каждый раз у меня будет хранится наибольший общий префикс для строк, которые у меня есть, а при добавлении новой, все пересчитывается во время добавления ее в бор. Код.
Если это такой же бесполезный алгоритм, скажите мне, как можно достичь такого же эффекта с онлайностью проще?
Наибольший общий префикс ещё можно было бы и обычными префикс или z-функциями считать. По времени и памяти это намного оптимальнее.
UPD.
Тьфу-блин. Да в лоб же спокойно за сумму длин решается. Каждую строку сравнить с образцом да и всё
О какой задаче вообще идет речь?
Если о задаче "найти LCP набора строк", то она ведь решается банальным for'ом за О(input) прямо в процессе считывания этого самого input'а. Изначально LCP равен первой строке, считывая следующую — уменьшаем ответ, если это необходимо.
А я было уже подумал, что нашлись единомышленники...
Любители стрелять из пушки по воробьям?)
А с какой именно целью Вы хотите сделать автомат персистентным? У меня есть некоторые наработки по теме (не прям про персистентность, но Вас заинтересовать могут).
Даже просто иногда хотелось бы удалить парочку символов с конца строчки, а как я понятия не имею. И плюс когда-то я видел задачу, где этим можно было ее просто решить.
Просто можно строить суфавтомат по бору, и это во многих случаях полезно. Если интересно — могу кинуть идею (но есть некоторые недоказанные аспекты насчет асимптотики).
Оки. Если не сложно сбросьте мне в личку идею. Мне очень интересно узнать.
Напишу лучше сюда.
Идея в том, что мы можем приписывать символ не только в конец, но и после произвольного префикса. Я ее уже публиковал на КФе, прямо перед инцидентом с выходом из строя SSD с базой данных.
Идея в разборе трех случаев:
Судя по всему, выполнять построение по бору нужно с помощью BFS-а, т.к. иначе можно получить квадрат (длинная строка из букв 'a', к каждой из которых приписана буква 'b', добавляем b-шки с конца). Если кто-то докажет асимптотику для алгоритма с BFS-ом, буду признателен.
Если нужен код — могу откопать и выложить.
Было бы неплохо посмотреть на код, ибо я не совсем доганяю, как это делать.
Задача H с текущего (XIV) сезона опенкапа, ГП Украины. Мое решение на нее. Самое интересное — функция
DAWG::add_char
. Строю автомат я неправильно — в худшем случае, описанном выше, будет квадрат.А кто-нибудь знает задачи на online judge, в которых нужно искать макс подстроку множества строк? Хотелось бы поупражняться :)
Задача А здесь
http://rosalind.info/problems/lcsm/
Вот блин, обрадовался было крутой задаче, а там, оказывается, надо несколько других прорешать, чтоб получить к ней доступ)
Или это можно обойти?
UPD: ну ладно, 5 простых задач решить не очень сложно. А там есть такая же задача, но с более строгими ограничениями?
UPD 2: Хотя куда уж строже)
http://www.e-olimp.com/problems/4802
Что Оо
Типичный e-olimp. Чего удивляться — то? Он тестирует нормально только при правильном расположении планет.
http://is.ifmo.ru/diploma-theses/_paraschenko-masters.pdf
Наибольший общий префикс в онлайне гораздо проще искать без бора. Вообще, нам даже не нужно запоминать сами строки. Достаточно помнить сам наибольший общий префикс и при добавлении новой строки пересчитывать его (он будет только укорачиваться).
А как проще всего решать эту задачу, если "в онлайне" — это не только добавления строк, но и удаления?
Мне на ум сразу лезет такая тупость: хранить отсортированный set, сравнивая строки при помощи хешей и бинпоиска.
Как на счет персистентного бора?
Если удаляется произвольная строка, а не последняя, то не поможет.
Заведем для каждой позиции массив, в котором будем записывать количество раз, которое встретился каждый символ на этой позиции. Пусть мы хотим удалить строку, тогда пройдем по всем ее позициям и уменьшим соответствующие счетчики. Теперь осталось найти последнюю такую позицию среди идущих подряд на префиксе, в которой символы во всех строках совпадают. Это можно сделать за O(1), если сохранить еще один массив, в котором для каждой позиции помнить следующую такую, где все символы совпадают. В итоге, нужно O(NΣ) памяти и в сумме это работает за O(N).
Спасибо. О(N) — здесь N это общий размер входных данных? А если надо обновлять быстрее, чем за длину?
Например, если среди запросов возможны запросы вида "добавить строку, которую добавляли в запросе х". Тогда уникальные входные строки могут иметь суммарную длину в пределах разумного, но весь инпут вообще — быть в много раз больше.
На ум приходит декартово дерево+хеши. В данном случае эта идея — примерно как бор yermak0v'а для LCP offline, и можно проще? Или не получится?
Можно с бором и каждый раз просто искать LCA нужного набора, построив над ним дерево отрезков, которое для каждого отрезка хранит LCA всех активных строк. Сложность, правда, будет похуже.