Самый длинный палиндром в строке с использованием дерева суффиксов

Я пытался найти самый длинный палиндром в строке. Решение грубой силы занимает O (n^3) времени. Я читал, что для него существует алгоритм линейного времени с использованием суффиксных деревьев. Я знаком с деревьями суффиксов и мне удобно их строить. Как вы используете построенное дерево суффиксов, чтобы найти самый длинный палиндром.

5 ответов


Я считаю, что нужно действовать таким образом:

пусть y1y2 ... yn быть строки (где yЯ письма).

создайте обобщенное дерево суффиксов Sf = y1y2 ... yn$ и Sr = ynyn-1 ... y1# (переверните буквы и выберите разные символы окончания для Sf ($) и Sr (#))... где Sf расшифровывается как "Строка", "Вперед" и Sr расшифровывается как "Строка, Обратный".

для каждого суффикса Я на Sf, найти наименьшего общего предка с суффиксом n-i + 1 на Sr.

что работает от корня до этого самого низкого общего предка палиндром, потому что теперь самый низкий общий предок представляет собой самый длинный общий префикс этих двух суффиксов. Вспомните, что:

(1) а префикс of a суффикс это подстрока.

(2) а палиндром - строка, идентичная ее обратной стороне.

(3) Таким образом, самый длинный содержащийся палиндром в строке является самой длинной общей подстрокой этой строки и ее обратной.

(4) таким образом, самый длинный палиндром в строке-это самый длинный общий префикс всех пар суффиксы между строкой и ее обратной. Вот что мы здесь делаем.

пример

давайте возьмем слово банан.

Sf = банан$

Sr = ananab#

Ниже приведено обобщенное дерево суффиксов Sf и Sr, где число в конце каждого пути является индекс соответствующего суффикса. Есть небольшая ошибка,a общие для всех 3 ветвей blue_4 родителя должны быть на его входящем краю, рядом с n:

enter image description here

низкий внутренний узел в дереве является самой длинной общей подстроки этой строки и наоборот. Глядя на все внутренние узлы дерево поэтому вы найдете самый длинный палиндром.

самый длинный палиндром находится между Green_0 и Blue_1 (т. е. банан и Анана) и Анана


EDIT

Я только что нашел этой статье это ответ на этот вопрос.


линейное решение можно найти следующим образом::

Prequisities:

(1).вы должны знать, как построить массив суффиксов в O(N) или O(NlogN) времени.

(2).вы должны знать, как найти стандартный массив LCP ie. LCP между смежными суффиксами i и i-1

ie . LCP [i]=LCP(суффикс i в отсортированном массиве, суффикс i-1 в отсортированном массиве) для (i>0).

пусть S будет Оригинальная строка и S' быть обратной исходной строки. Возьмем S="банан" как пример. Тогда его обратная строка S ' =ananab.

Шаг 1: Concatenate S + # + S' чтобы получить String Str, где # - алфавит, отсутствующий в исходной строке.

    Concatenated String Str=S+#+S'
    Str="banana#ananab"

Шаг 2: теперь построим массив суффиксов строки Str.

в этом примере массив суффиксов есть:

Suffix Number   Index   Sorted Suffix
0               6       #ananab
1               5       a#ananab
2               11      ab
3               3       ana#ananab
4               9       anab
5               1       anana#ananab
6               7       ananab
7               12      b
8               0       banana#ananab
9               4       na#ananab
10              10      nab
11              2       nana#ananab
12              8       nanab

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

что это SuffixArray[]={6,5,11,3,9,1,7,12,0,4,10,2,8};

Шаг 3: как вам удалось построить массив суффиксов, теперь найдите самые длинные общие префиксы между смежными суффиксы.

LCP between #ananab        a#ananab          is :=0
LCP between a#ananab       ab                is :=1
LCP between ab             ana#ananab        is :=1
LCP between ana#ananab     anab              is :=3
LCP between anab           anana#ananab      is :=3
LCP between anana#ananab   ananab            is :=5
LCP between ananab         b                 is :=0
LCP between b              banana#ananab     is :=1
LCP between banana#ananab  na#ananab         is :=0
LCP between na#ananab      nab               is :=2
LCP between nab            nana#ananab       is :=2
LCP between nana#ananab nanab                is :=4

таким образом, массив LCP LCP={0,0,1,1,3,3,5,0,1,0,2,2,4}.

где LCP[i]=длина самого длинного общего префикса между суффиксом i и суффиксом (i-1). (для i>0)

Шаг 4:

теперь, когда вы построили массив LCP, используйте следующую логику.

    Let the length of the Longest Palindrome ,longestlength:=0 (Initially)
    Let Position:=0.
    for(int i=1;i<Len;++i)
    {
        //Note that Len=Length of Original String +"#"+ Reverse String
        if((LCP[i]>longestlength))
        {
            //Note Actual Len=Length of original Input string .
            if((suffixArray[i-1]<actuallen && suffixArray[i]>actuallen)||(suffixArray[i]<actuallen && suffixArray[i-1]>actuallen))
            {
                 //print :Calculating Longest Prefixes b/w suffixArray[i-1] AND  suffixArray[i]


                longestlength=LCP[i];
              //print The Longest Prefix b/w them  is ..
              //print The Length is :longestlength:=LCP[i];
                Position=suffixArray[i];
            }
        }
    }
    So the length of Longest Palindrome :=longestlength;
    and the longest palindrome is:=Str[position,position+longestlength-1];

Исполнение Пример:

    actuallen=Length of banana:=6
    Len=Length of "banana#ananab" :=13.

Calculating Longest Prefixes b/w a#ananab AND  ab
The Longest Prefix b/w them  is :a 
The Length is :longestlength:= 1 
Position:= 11




Calculating Longest Prefixes b/w ana#ananab AND  anab
The Longest Prefix b/w them  is :ana
The Length is :longestlength:= 3 
Position:=9



Calculating Longest Prefixes b/w anana#ananab AND  ananab
The Longest Prefix b/w them  is :anana
The Length is :longestlength:= 5 
Position:= 7

So Answer =5.
And the Longest Palindrome is :=Str[7,7+5-1]=anana

просто отметьте::

условие if на шаге 4 в основном это означает, что в каждой итерации (i), Если я беру суффиксы s1 (i) и s2(i-1), то"s1 должен содержать # и s2 не должен содержать #" или "С2 должны содержать # и С1 не содержит # ".

 |(1:BANANA#ANANAB)|leaf
tree:|
     |     |      |      |(7:#ANANAB)|leaf
     |     |      |(5:NA)|
     |     |      |      |(13:B)|leaf
     |     |(3:NA)|
     |     |      |(7:#ANANAB)|leaf
     |     |      |
     |     |      |(13:B)|leaf
     |(2:A)|
     |     |(7:#ANANAB)|leaf
     |     |
     |     |(13:B)|leaf
     |
     |      |      |(7:#ANANAB)|leaf
     |      |(5:NA)|
     |      |      |(13:B)|leaf
     |(3:NA)|
     |      |(7:#ANANAB)|leaf
     |      |
     |      |(13:B)|leaf
     |
     |(7:#ANANAB)|leaf

С опозданием на несколько лет...

предположим s является исходной строкой, и r is s отменено. Предположим также, что мы полностью построили дерево суффиксов ST используя s.

наш следующий шаг-проверить все суффиксы r против ST. С каждым новым суффиксом r, мы будем поддерживать счет первого k символы, которые мы успешно сопоставили с уже существующим суффиксом в дереве (т. е. одним из s'ы суффиксы.)

в качестве примера, скажем, мы сопоставляем суффикс "крыса" С r и s содержит некоторые суффиксы начиная с "РА", но ни один из них не соответствовал "крыса". k будет равно 2, когда нам, наконец, пришлось отказаться от надежды на окончательные символы "Т". Мы сопоставили первые два символа rсуффикс с первыми двумя символами sсуффикса-ов. Мы назовем этот узел, который мы достигли n.

проверка всех узлов листьев под n.

в традиционном дереве суффиксов начальный индекс каждого суффикса хранится в узле листа этой ветви суффикса. В нашем примере выше, s возможно, содержит кучу суффиксов, начинающихся с "РА", каждый из которых начинается с одного из индексов, присутствующих в потомках листового узла n.

давайте использовать эти индексы.

что это значит, если мы Матч k героев Rподстроки против k символы ST? Ну, это просто значит, что мы нашли какую-то обратную струну. Но что это значит, если место, где начинается подстрока в R равна подстроку в S плюс k? Да, это значит s[i] through s[i+k] читается так же, как s[i+k] through s[i]! И так, be definition мы обнаружили палиндром размер k.

теперь все, что вам нужно сделать, это сохранить вкладку на самом длинном палиндроме, найденном до сих пор, и вернуть его в конце вашей функции.


простое и короткое объяснение от Skiena - The Algorithm Design Manual

найти самый длинный палиндром в S [с помощью суффиксного дерева] - A палиндром - это строка, которая читает то же самое, если порядок символов отменен, например мадам. Чтобы найти самый длинный палиндром в строке S, постройте одно дерево суффиксов, содержащее все суффиксы S и разворот S, с каждым листом, идентифицированным по его начальной позиции. Палиндром определяется любым узлом в этом дереве что вперед и вспять дети с той же позиции.


решение DP:

int longestPalin(char *str)
{
    n = strlen(str);
    bool table[n][n]l
    memset(table, 0, sizeof(table));
    int start = 0;

    for(int i=0; i<n; ++i)
        table[i][i] = true;
    int maxlen = 1;

    for(int i=0; i<n-1; ++i)
    {
        if(str[i] == str[i+1])
        {
            table[i][i] = true;
            start = i;
            maxlen = 2;
        }
    }

    for(int k=3; k<=n; ++k)
    {
        for(int i=0; i<n-k+1; ++i)
        {
            int j = n+k-1;
            if(str[i] == str[j] && table[i+1][j-1])
            {
                table[i][j] = true;
                if(k > maxlen)
                {
                    start = i;
                    maxlen = k;
                }
            }
        }
    }
    print(str, start, start+maxlen-1);
    return maxlen;
}