下路认真看了半个小时,才看懂了。。。(:зゝ∠)
看来还是太弱了(。・・)ノ
老套路,一道题目,引入算法。
链接http://acm.hdu.edu.cn/showproblem.php?pid=3068
给你一堆字符串,让你判断其最长的回文子串的长度。
回文子串是啥就不用说了吧。。。。
首先不得不说这个算法吼巧妙O(∩_∩)O~
首先对于一个字符串s=abcdsdcabc,长度有奇偶两种情况。
这个算法规定在每一个字符前后插入一个同样的字符。
如上述更换后,s=#a#b#c#d#s#d#c#a#b#c#。
这样将字符串都转化为奇数处理了。(o_ _)ノ
为了方便处理越界情况,我们在前面加一个特殊的符号。
于是最终变成s=$#a#b#c#d#s#d#c#a#b#c#(似乎变成了偶数。。)
然后我们定义一个数组p[i],表示当前以i为回文串的中心,向右最多延伸的长度。
那么上面的
s= $#a#b#c#d#s#d#c#a#b#c#
p=1,1,2,1,2,1,2,1,4,1,2,1,2,1,2,1,2,1,2,1,2,1
p[i]=1就表示不能延伸,只能是自己。
可以发现max(p[i])-1就是原串中的最长回文子串。
我们的目的就是在短时间内求出p[i]。
我们算法是从前往后扫,就是说我们求p[i]时,p[1~i-1]都是求出来了。
我们设mx表示在前i-1中,回文串向右延伸到最长的位置,并把这个回文串的中心位置记为id。
那么id+p[id]就是这个回文串向右最长延伸到的位置。
就是我们求i时,如果mx>i,意思就是当前i处于id为中心的回文串的范围内。
1.那么对于id这个回文串有s[id+1~p[id]]=s[id-1~p[id]],所以i 与 i关于id的对称点j是一样的,那么以j为中心的字符串我们已经求得了,那么j的情况自然就是i的情况。所以直接得p[i]=p[j];
2.j这个回文串向右延伸的长度为p[j],但i+p[j]可能已经大于mx了,那么这就是不合法的,因为mx后面的情况我们都不知道。此时p[i]=mx-i;
综上:p[i]=min(p[j],mx-i)。别问我j怎么求╭(╯^╰)╮。
这幅图其实说的很清楚了。
这个算法复杂度是O(n)的。很优秀。
需要注意的是新字符串和p的数组的范围。
#include
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1e5+10035;
char ch[maxn],now[maxn*2];
int p[maxn*2],ans;
int main()
{
freopen("T.in","r",stdin);
while (scanf("%s",&ch)!=EOF){
int len=0,len1=strlen(ch);
now[0]='$';
fo(i,0,len1-1){
now[++len]='#';
now[++len]=ch[i];
}
now[++len]='#';
getchar();
p[0]=1; ans=0;
int id=0,mx=1;
fo(i,1,len){
if (mx>i) p[i]=min(p[2*id-i],mx-i);
else p[i]=1;
while (now[i+p[i]]==now[i-p[i]]) ++p[i];
if (p[i]+i>mx){
mx=p[i]+i;
id=i;
}
if (p[i]>ans) ans=p[i];
}
printf("%d\n",ans-1);
}
return 0;
}