[Manacher]最長迴文子串

好久沒有寫博客了
啪啪啪
寫一些東西吧算法

最長迴文子串怎麼求呢
首先咱們得知道什麼是子串,給你一個長長的串,裏面任意連續的一段就是它的子串,固然一個字符也是子串
接着什麼是迴文串呢 很差描述 可是看例子很容易懂:aba 121 1221 1
而後咱們有一種很顯然的尋找方法 固然是枚舉中點 而後儘量的往外擴大回文串的長度 這種算法花費的時間是N(字符串長度)*Mk(可擴展長度)Mk<Ncode

固然這樣的時間並非很讓人滿意
因而Manacher這我的發明了一種新算法
他是這樣想的
假如我先前已經找到的迴文串已是很大了,好比已經覆蓋了整個字符串那麼長
那我找到它之後 就不要找了啊 根據迴文串左右對稱的特性
咱們能夠利用以前找到的可擴展的右端,以及擴展起始點
若是當前查找的被擴展最右端覆蓋,那麼當前查找的起始點可擴展的最小值就是以擴展起始點爲對稱中心相對稱的那個點的擴展值
若是擴展到了邊界,就能夠繼續擴展下去直到不能擴展,啪啪啪就結束了ci

很是精彩 可是有個問題 中點多是一個字符 也能夠是兩個字符
分狀況討論?費勁
有一種很機智的方法 往字符串每一個字符間摻同一個不屬於這個字符串的字符而後先後各摻一個
顯然假如字符串有n個 新字符串必爲2*n+1 也就是隻須要討論一種狀況了
至於到底要求長度仍是求字符 就在求的時候開個最大值記錄的存一下就好了字符串

這個算法的時間花費是4*N(字符串長度)(建一遍,找一遍,長度由於擴大了一倍因此是2N)get

下面是個打得很爛的模板博客

//Manacher算法
string s;
char inser='&';
char str[301000];
int len[301000];
void cvt(int n){
    str[0]=inser;
    for(int i=1;i<=(n<<1);i+=2){
        str[i]=s[i>>1];
        str[i+1]=inser;//cout<<str[i]<<" "<<str[i+1]<<" ";
    }
}
void getLen(int l){
    int p0=0,p=0;len[0]=1;
    int j;
    for(int i=1;i<=l;i++){
        if(i<=p){
            j=(p0<<1)-i;
            if(len[j]<(p-i))len[i]=len[j];
            else len[i]=p-i;
//          len[i]=min(len[2*p0-i],p-i);
        }
        else
            len[i]=1;
        int cl=i-len[i],cr=i+len[i],temp=len[i];
        while(cl>=0&&cr<=l&&str[cl]==str[cr]){
        //  if(str[cr]!=inser&&str[cr]>str[cr-2])break;
            temp++;
            cl--;cr++;
        }
        len[i]=temp;
        if((i+len[i]-1)>p){
            p=i+len[i]-1;
            p0=i;
        }
    }
}
int main(){
    while(cin>>s){
        int n;
        n=s.size();
        cvt(n);
        getLen(n<<1);
        int ans=0;
        for(int i=0;i<=(n<<1);i++)
        ans=max(ans,len[i]);
        cout<<ans-1<<endl;
    }
    return 0;
}
相關文章
相關標籤/搜索