Пожалуйста, прочтите новое правило об ограничении использования AI-инструментов. ×

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

Автор yermak0v, 10 лет назад, По-русски

Только что я сидел разбирался с суффиксным автоматом (вроде бы все понятно и пишу норм, но если долго не писать забывается) и в примере задач увидел задачу из заголовка.
Ссылка на статью на e-maxx'e, там в примере задач она последняя.
Ну в общем убил я где-то минут 30-40 на то чтоб понять это, но толку нет. Не все так просто как казалось. Увы...
Но буквально пару минут я вспомнил о боре, и подумал: "а чего я хочу этого именно от суффиксного автомата, если это можно сделать в разы проще бором?"
Моя идея в том чтобы просто забросить все строчки в бор, а потом обходом в глубину (хотя зачем?) просто переходить по состояниям пока есть переход только по одной букве, иначе понятно, что уже общего префикса не будет. Идея проще простого, да и написать это очень легко.
Вот код.
Upd. Как же я люблю тупить... На e-maxx'e описано решение задачи о нахождении наибольшей общей подстроки. А префикс можно искать намного проще даже втупую, хотя втупую придется каждый раз пробегать за количество строк, чтобы сравнить равны ли соответствующие символы. Но все же этот алгоритм вполне можно использовать.
Извиняюсь за тупку, не судите строго.
Upd2. Да и идея наверное далеко не новая.
===========================================
И пользуясь моментом пару вопросов о автомате:
-Где можно было бы почитать о наибольшей общей подстроке нескольких строк, чтобы точно понять это?
-И раз уж пошел разговор о автомате, то можно ли, и как, сделать его персистентным? Т.е. добавлять символы очень удобно, но хотелось бы и уметь удалять их, хотя бы для начала по одному.
===========================================
Ура, я таки нашел в чем польза от этого способа. Немного изменив код, я пришел к онлайновости этого алгоритма. Каждый раз у меня будет хранится наибольший общий префикс для строк, которые у меня есть, а при добавлении новой, все пересчитывается во время добавления ее в бор. Код.
Если это такой же бесполезный алгоритм, скажите мне, как можно достичь такого же эффекта с онлайностью проще?

  • Проголосовать: нравится
  • +2
  • Проголосовать: не нравится

»
10 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

Наибольший общий префикс ещё можно было бы и обычными префикс или z-функциями считать. По времени и памяти это намного оптимальнее.

UPD.

Тьфу-блин. Да в лоб же спокойно за сумму длин решается. Каждую строку сравнить с образцом да и всё

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится +12 Проголосовать: не нравится

    О какой задаче вообще идет речь?

    Если о задаче "найти LCP набора строк", то она ведь решается банальным for'ом за О(input) прямо в процессе считывания этого самого input'а. Изначально LCP равен первой строке, считывая следующую — уменьшаем ответ, если это необходимо.

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    А я было уже подумал, что нашлись единомышленники...

»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

А с какой именно целью Вы хотите сделать автомат персистентным? У меня есть некоторые наработки по теме (не прям про персистентность, но Вас заинтересовать могут).

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Даже просто иногда хотелось бы удалить парочку символов с конца строчки, а как я понятия не имею. И плюс когда-то я видел задачу, где этим можно было ее просто решить.

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Просто можно строить суфавтомат по бору, и это во многих случаях полезно. Если интересно — могу кинуть идею (но есть некоторые недоказанные аспекты насчет асимптотики).

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится +3 Проголосовать: не нравится

        Оки. Если не сложно сбросьте мне в личку идею. Мне очень интересно узнать.

        • »
          »
          »
          »
          »
          10 лет назад, # ^ |
          Rev. 2   Проголосовать: нравится +5 Проголосовать: не нравится

          Напишу лучше сюда.

          Идея в том, что мы можем приписывать символ не только в конец, но и после произвольного префикса. Я ее уже публиковал на КФе, прямо перед инцидентом с выходом из строя SSD с базой данных.

          Идея в разборе трех случаев:

          1. Перехода по букве нет — выполняем стандартный алгоритм добавления символа с е-макса
          2. Есть сплошной переход по букве — просто двигаем указатель
          3. Есть несплошной переход — выполняем клонирование вершины

          Судя по всему, выполнять построение по бору нужно с помощью BFS-а, т.к. иначе можно получить квадрат (длинная строка из букв 'a', к каждой из которых приписана буква 'b', добавляем b-шки с конца). Если кто-то докажет асимптотику для алгоритма с BFS-ом, буду признателен.

          Если нужен код — могу откопать и выложить.

          • »
            »
            »
            »
            »
            »
            10 лет назад, # ^ |
              Проголосовать: нравится +3 Проголосовать: не нравится

            Было бы неплохо посмотреть на код, ибо я не совсем доганяю, как это делать.

            • »
              »
              »
              »
              »
              »
              »
              10 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится

              Задача H с текущего (XIV) сезона опенкапа, ГП Украины. Мое решение на нее. Самое интересное — функция DAWG::add_char. Строю автомат я неправильно — в худшем случае, описанном выше, будет квадрат.

»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

А кто-нибудь знает задачи на online judge, в которых нужно искать макс подстроку множества строк? Хотелось бы поупражняться :)

»
10 лет назад, # |
  Проголосовать: нравится +29 Проголосовать: не нравится

Наибольший общий префикс в онлайне гораздо проще искать без бора. Вообще, нам даже не нужно запоминать сами строки. Достаточно помнить сам наибольший общий префикс и при добавлении новой строки пересчитывать его (он будет только укорачиваться).

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    А как проще всего решать эту задачу, если "в онлайне" — это не только добавления строк, но и удаления?

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Мне на ум сразу лезет такая тупость: хранить отсортированный set, сравнивая строки при помощи хешей и бинпоиска.

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Как на счет персистентного бора?

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Если удаляется произвольная строка, а не последняя, то не поможет.

    • »
      »
      »
      10 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      Заведем для каждой позиции массив, в котором будем записывать количество раз, которое встретился каждый символ на этой позиции. Пусть мы хотим удалить строку, тогда пройдем по всем ее позициям и уменьшим соответствующие счетчики. Теперь осталось найти последнюю такую позицию среди идущих подряд на префиксе, в которой символы во всех строках совпадают. Это можно сделать за O(1), если сохранить еще один массив, в котором для каждой позиции помнить следующую такую, где все символы совпадают. В итоге, нужно O(NΣ) памяти и в сумме это работает за O(N).

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Спасибо. О(N) — здесь N это общий размер входных данных? А если надо обновлять быстрее, чем за длину?

        Например, если среди запросов возможны запросы вида "добавить строку, которую добавляли в запросе х". Тогда уникальные входные строки могут иметь суммарную длину в пределах разумного, но весь инпут вообще — быть в много раз больше.

        На ум приходит декартово дерево+хеши. В данном случае эта идея — примерно как бор yermak0v'а для LCP offline, и можно проще? Или не получится?

        • »
          »
          »
          »
          »
          10 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

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