[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;
}
相关文章
相关标签/搜索