manacher

应用

O(n)求以每个节点为中心的回文串长度

原理

1、对S=“hshbvbhshb”,每个字符之间插入“#”,以便统一奇偶长度回文串。并在最前插入"^",左右扩展边界时才不会访问到-1,右边不用是因为自带"\0"。得到T="^#h#s#h#b#v#b#h#s#h#b#"

2、定义p[i]为T中i位为中心回文串半径,注意回文串不包括边界的"#"。然后我们愉快地发现p[i]就是S中回文串长度,而起点就是(i-p[i]-1)/2

3、最最最重要:如何求p[i]   利用回文串对称性质

假设我们已经知道i位前的p[],现回文串最右的右端点为R(注意R不在回文串中),其中心为C。i_m为i对于C对称点

若i不在C的回文串里,朴素

找到对称点回文半径3,那么i为回文半径至少为3,即至少有回文串"h#s#h"

为什么说至少:i回文右端"h"右接C的回文串外部,外部的情况是不确定的,是未知的。朴素地扩展一下,同时更新C,R

不至少的情况:i回文不右接外部,即p[i_m]<diff=R-i

上面说的至少也不准确,因为有i_m回文串超出C回文串的情况。因此p[i]至少为min(p[i_m],diff)

时间复杂度是怎么保证的呢?在每次朴素的时候,我们将R单调向右移动,最多移动n次

代码

int p[MAX];
string s;
inline string pP(string s){
    int n=s.length();
    if(!n)  return "^";
    string ret="^";
    for(int i=0;i<n;++i)
        ret+="#"+s.substr(i,1);
    ret+="#";
    return ret;
}inline void lP(string s){
    string T=pP(s);int len=T.length();
    int C=0,R=0;
    for(int i=1;i<len-1;++i){
        int i_m=C*2-i,diff=R-i;
        if(p[i_m]<diff)  p[i]=p[i_m];
        else{
            p[i]=max(0,diff);
            while(T[i+p[i]+1]==T[i-p[i]-1]){
                p[i]++;C=i;R=i+p[i];
            }
        }
    }
}

 

posted @ 2023-07-29 08:21  yisiwunian  阅读(22)  评论(0编辑  收藏  举报