Manacher算法笔记

来源:互联网 发布:域服务器软件分发 编辑:程序博客网 时间:2024/05/22 15:03

定义:

一种简单的寻找回文子串的算法。可以在O(N)的时间复杂度内,求出以每个字母为对称中心的回文子串。

属于奇技淫巧?回文串的题很少啊。
我们还有后缀数组求回文子串的方法,可以做到O(Nlog2N),但是写起来烦,(主要是我不会写)。

模板

#define min(a,b) ((a)<(b)?a:b)void Manacher(char *a) {    int MaxId,id;    for(int i=1;a[i]!='\0';i++)      {         if(MaxId>i) p[i]=min(p[2*id-i],MaxId-i);         else p[i]=1;         while(a[i+p[i]]==a[i-p[i]]) p[i]++;         if(p[i]+i>MaxId){               MaxId=p[i]+i;               id=i;           }      } }/*以下是构造的片段*/for(i=1;b[i]!='\0';i++)    {          a[i<<1]=b[i];          a[(i<<1)+1]='#';    }a[0]='?',a[1]='#';n=(i<<1)+2,a[n]=0;

说明

定义一些符号:

原串:S0
处理后保证奇数长度的串:S
反串:S(即为S前后倒置的串)
数组:P 记录以每个字符为中心的最长回文半径的数组(最小为1,即只含有其本身)

预处理

S0两个两个字符之间插入不属于原字符集的字符,将其转化为S

引理

P[i]1是以Si为中心的回文子串在原串中的长度。

算法

我们接下来还要再定义两个数字,即:

MaxId 为之前的回文串中匹配到的最远位置
id 就是MaxId被取到时的i

对于i=2idi,容易看出ii关于id对称。但如果这一部分的回文串,翻过去超出了MaxId,那我们就不能保证原有的对称性。那么为了保证正确性,我们在这两种可能性中取最小,最终我们有P[i]=min{P[2idi],MaxIdi}。但此时的P[i]不是最佳解,而只是最小值。我们通过迭代,可以求到P[i]的最优解。最终统计时,统计P数组的最大值即可。

HDU3068

裸题。
(也没什么好题了)

#include <stdio.h>#define M 110010char b[M],a[M<<1];int p[M<<1];#define min(a,b) ((a)<(b)?a:b)#define max(a,b) ((a)>(b)?a:b)int main()  {    int i,n,id,MaxL,MaxId;    while(scanf("%s",b+1)!=EOF)     {        MaxL=MaxId=0;        for(i=1;b[i]!='\0';i++)         {            a[i<<1]=b[i];            a[(i<<1)+1]='#';           }        a[0]='?',a[1]='#';        n=(i<<1)+2,a[n]=0;        for(i=1;i<n;i++)         {            if(MaxId>i) p[i]=min(p[2*id-i],MaxId-i);            else p[i]=1;            while(a[i+p[i]]==a[i-p[i]]) p[i]++;            if(p[i]+i>MaxId) {                  MaxId=p[i]+i;                  id=i;             }            MaxL=max(MaxL,p[i]);         }        printf("%d\n",MaxL-1);      } }
0 0
原创粉丝点击