1. 程式人生 > >求最大回文子串(Manacher演算法)

求最大回文子串(Manacher演算法)

這個知識點我第一天聽時,完全不懂,後來慢慢的看一個pdf文件和請教一個學長才有點懂得,到今天我繼續看一篇部落格,才對最大回文子串有清晰的理解,所以上面有什麼不對的請給位積極指出。

求最大回文子串,我個人覺得其實就是一種想法(它用到了動態規劃的思想),還不算一種單獨的演算法。。。。。。

首先,迴文串是一個正讀和反讀都是一樣的字串,比如abba,abcdadcba等等

迴文子串就是在一個字串中滿足迴文串概念的一個或者是多個子串(包括它自身)

最簡單最直接的想法就是從頭到尾遍歷,分別以每一個字元為中心向兩側擴充套件,算出以每一個字元下為中心所包含的迴文串的個數(包括自身的)。雖然這種方法很好想,但是它的時間複雜度為O(n^2),這個時間是很低的。

所以,我們就想到了一種只需要O(n)的時間複雜度的方法

用這種方法求最大回文子串,首先需要對原字串進行預處理,因為在以前求迴文串時,需要判斷奇偶的情況。預處理就是在一串字串中每相鄰兩個字元之間都新增上一個’#’(字串的最前面和最後面都也要加上一個字元’#’,還有,最前面的最前面的加一個字元是防止在求p[i]時向兩邊擴充套件可能引起陣列越界)

字元新增好後,最重要的一點就是如何求p[i]陣列的值?

首先,p[i]陣列表示的含義是,記錄的以每一個字元為中心的最長迴文串的半徑(以每一個字元為中心能組成的迴文子串的個數),也就是記錄以S[I]為中心的最長的迴文子串的半徑)

P[i]陣列的求法,要借鑑動態規劃的思想

舉例:求abaab的最大回文子串?


首先,我們先定義兩個變數,一個maxid表示在求i之前的迴文串中,能延伸到最右端的位置,同時好有一個id記錄取這個maxidid

Maxid=p[id]+id

分兩種情況:

1、  第i個位置不在前面的任何迴文串中,即maxid<i,則初始化p[i]=1,然後左右延伸,就是while(s[p[i]+i]==s[i-p[i]])這句話

2、  如果maxid>i,那麼就不是初始化p[i]=1,就要由迴文串的性質,在id位置前還應該有一個j位置,它是關於id位置對稱。可以對稱到i位置

如圖:


或者是



其實,就是把它們看成一個一維的座標來看,

j就可以表示成j=2*id-i,由中點座標公式得來的;如果不用中點座標公式,可以用幾何關係推出來,i-2*i-id);

所以,p[i]又可以分出3種情況:

1、以j為中心的迴文串的範圍有一部分在id為中心的迴文串範圍之外,所以推出,maxid-i=p[i]-k(k為i和maxid之間的距離)

2、以j為中心的迴文串的範圍全部在id為中心的迴文串範圍之內,所以推出,p[i]=p[j]

3、j的迴文串的左端部分與id的迴文串的左端部分重疊,即j-p[j]=i-p[i],所以p[j]=p[i],並且p[i]可以繼續增加,需要while(s[p[i]+i]==s[i-p[i]];p[i]++;這句話來控制


程式碼:

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<iostream>

#include<algorithm>

using namespace std;

const int N=1010;

char s1[N],s2[2*N];

int p[2*N];

void Init(int len)

{

   s2[0]='*',s2[len*2+1]='#';

   for(int i=0;i<len;i++)

    {

       s2[i*2+1]='#';

       s2[i*2+2]=s1[i];

    }

}

int Solve(int len)

{

   int maxid=0,maxl=0,id;

   for(int i=1;i<=len*2+1;i++)

    {

       if(maxid>i)

           p[i]=min(p[2*id-i],maxid-i);

       else

           p[i]=1;

       while(s2[i+p[i]]==s2[i-p[i]])

           p[i]++;

       if(p[i]+i>maxid)

       {

           maxid=p[i]+i;

           id=i;

       }

       if(p[i]>maxl)

           maxl=p[i];

    }

   return maxl-1;

}

int main()

{

   while(~scanf("%s",s1))

    {

       int len=strlen(s1);

       Init(len);

       int l=Solve(len);

       printf("%d\n",l);

    }

   return 0;

}