http://hi.baidu.com/735612658gfy/blog/item/f88114d526dc39bdcc1166db.html

http://acm.uestc.edu.cn/bbs/read.php?tid=3258

回文串可以分为两种情况,一种是长度为奇数如abcba,另一种是偶数的串,如abba。这样就为解决问题带来了不便,处理的时候需要分类讨论,为了避免这种情况,一种巧妙地方法就是对这中串进行处理,在每个字符前加一个‘#’,而第一个字符另赋值一个字符,如‘$’。这样,回文串的个数就都为奇数个了,如 ababcbaba就成了#a#b#c#b#a#,abba就成了#a#b#b#a#,然后用一个辅助数组rad记录以每个字符为中心的最长回文串的信息,rad[i]表示回文的半径,而rad[i]-1即为以str[i]为中心的回文字串在原串中的长度

原串: w aa bwsw f d
新串: # w # a # a # b # w # s # w # f # d #
辅助数组rad: 1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1

在网上看到别人的总结

我们规定p[i]是从第i个字符开始回文的半径。其实以这个字符在原来串的回文个数就是p[i]-1;

当我们求p[i]时,在i之前的p【j】是求出的,我们记录了mx=max(p【j】+j)。

当i<ma时。p【i】=min(p[2*j-i],ma-i);每次这个时间是O(1)的。

几个例题:

参见:

最长回文子串模板,Manacher算法,时间复杂度 O(n)

//hdu3068
View Code
#include<iostream>
#include<cstdio>
#include<stdio.h>
#include<math.h>
#include<cstring>
using namespace std;

#define M 20000050

char str1[M],str[2*M];
int rad[M],nn,n;

void Manacher(int *rad,char *str,int n)

//*str是这样一个字符串(下标从1开始):
//举例:若原字符串为"abcd",则str为"$#a#b#c#d#",最后有个终止符.
//n为str的长度,若原字符串长度为nn,则n=2*nn+2;
//rad[i]表示回文的半径,即最大的j满足str[i-j+1……i]=str[i+1……i+j],
//而rad[i]-1即为以str[i]为中心的回文字串在原串中的长度
{
int i;
int mx=0;
int id;
for(i=1;i<n;i++)
{
if(mx>i)
rad[i]=rad[2*id-i]<mx-i?rad[2*id-i]:mx-i;
else
rad[i]=1;
for(;str[i+rad[i]]==str[i-rad[i]];rad[i]++);
if(rad[i]+i>mx)
{
mx=rad[i]+i;
id=i;
}
}
}

int main()
{
int i,ans;
while(scanf("%s",str1)!=EOF)
{
nn=strlen(str1);
n=2*nn+2;
str[0]='$';
for(i=0;i<=nn;i++)
{
str[2*i+1]='#';
str[2*i+2]=str1[i];
}
Manacher(rad,str,n);
ans=1;
for(i=0;i<n;i++)
ans=rad[i]>ans?rad[i]:ans;
printf("%d\n",ans-1);
}
return 0;
}


如果不用词算法,可以就奇偶分情况计算
View Code
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

#define N 5010

char buf[N],s[N];
int p[N];

int main()
{
int i,j,m,len,max,x,y,T;
scanf("%d",&T);
getchar();
while(T--)
{
gets(buf);
len=strlen(buf);
//p里面存储的是转换为大写字母并且去掉不是字母后的字符串中个字符对应原串的下标
for(i=0,m=0;i<len;i++)
{
if(isalpha(buf[i]))//判断是否为a,b,c....这样的字母
{
s[m]=tolower(buf[i]);
p[m++]=i;
}
}
max=0;
for(i=0;i<m;i++)
{
//奇数时,每次向外扩展长度为1,即j+1,则向外扩展了两个字符,故为2*j,再加上中间点,所以是2*j+1
for(j=0;i-j>=0&&i+j<m;j++)
{
if(s[i-j]!=s[i+j])
break;
if(j*2+1>max)
{
max=j*2+1;
x=p[i-j];
y=p[i+j];
}
}
//若为偶数时,则中间点有两个,即:当前字符及其下一个字符,故为2*j+2;
for(j=0;i-j>=0&&i+j+1<m;j++)
{
if(s[i-j]!=s[i+j+1])
break;
if(j*2+2>max)
{
max=j*2+2;
x=p[i-j];
y=p[i+j+1];
}
}
}
for(i=x;i<=y;i++)
printf("%c",buf[i]);
printf("\n");
}
return 0;
}

/* 只需每次枚举回文字符串的“中间位置”i,然后不断往外扩展,直到有字符不同。 提示:长度为奇数和偶数的处理方式不一样。具体点是 每次以字符串中当前字母为中间点,向外扩展。 若为奇数,则以此处一个字符为中间点向外扩展,若为偶数,则以此点及其下一点为中间点向外扩展。 */
posted on 2012-03-23 15:11  pcoda  阅读(276)  评论(0编辑  收藏  举报