manacher算法

manacher,是一种线性时间复杂度求解回文字符串的算法。而且很短。

首先我们观察回文串,发现有长度为奇数的(以某个字符为对称中心)和长度为偶数的(以两个字符间的空位为对称中心)。为了方便我们把它们转化成一样的,我们在字符之间插入一个占位符#,这样所有回文串就变成了奇数长度。

然后观察转移的方式。我们根据回文串的对称性质,可以用对称中心左边已经处理过的字符的回文半径f(即从对称中心到回文串一边的字符个数)转移对称中心右边与它对称的回文半径。

sl sjf[j]+1  sj  sj+f[j]1 palindrome  sif[j]+1  si  si+f[j]1palindrome srpalindrome

(我现在才知道oiwiki的这个看起来像图的东西是个LATEX

观察可以得到,左边的sj和右边的si对称,所以可以将f[j]直接转移到f[i]。但是还有这样一种情况,就是我们左边的回文串超过了与当前回文串左端点的距离,那么就只能将右边的回文串转移到右端点的位置,而不能直接转移f[j]

最后,为了尽可能转移更多的字符,我们需要找到右端点在最右边的一个回文串。这个保存一个指针扫就可以。

上个代码。

int main(){
    scanf("%s",ch+1);len=strlen(ch+1)*2+1;//一定要+1保证头尾都是#
    int r=0,mid=0;s[0]='~';//s[0]插入不同字符保证数组不越界
    for(int i=1;i<=len;i++)s[i]=(i&1)?'#':ch[i>>1];//在字符中插入占位符
    for(int i=1;i<=len;i++){
        if(i<=r)f[i]=min(f[2*mid-i],r-i+1);//更新答案
        while(s[i+f[i]]==s[i-f[i]])f[i]++;//暴力扩展
        if(i+f[i]>r)r=i+f[i]-1,mid=i;//更新指针
        ans=max(ans,f[i]);
    }
    printf("%d",ans-1);/*回文串真实长度
    这个可以分类讨论一下 如果真实长度是奇数那么占位符一定比字符多1
    而(字符数+占位符数)=2*回文半径-1
    把+1和*2扔到左边 因为是奇数然后+1 /2答案就大了1 所以要-1
    然后考虑偶数的情况 占位符仍然比字符多1 可以按类似的方法计算*/
}

这个算法的时间复杂度为什么是O(n)的呢?观察我们外层循环的i指针是扫了一遍的,而我们指向回文串最右端的r指针随着扫描而单调递增,所以两个指针都只线性扫了一遍,所以是O(n)的。

posted @   gtm1514  阅读(25)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示