定义:
一种简单的寻找回文子串的算法。可以在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 。
引理
算法
我们接下来还要再定义两个数字,即:
MaxId 为之前的回文串中匹配到的最远位置
id 就是 MaxId 被取到时的 i 点
对于
HDU3068
裸题。
(也没什么好题了)
#include <stdio.h>
#define M 110010
char 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);
}
}