HDU - 3068 最长回文(manacher)
HDU - 3068
Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理) 字符串长度len <= 110000 Output 每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
Sample Input aaaa abab Sample Output 4 3 Source |
题目要求求出最长回文子串,是一道manacher的模板题。
我们先讲一下manacher,我们先处理一下字符串,把字符串处理成#a#a#这样的格式,比如abcd处理成#a#b#c#d#,这样的好处是解决了判断回文串是奇数和偶数的问题,把回文串强制变成奇数。接下来,定义一个p[]数组,p[i]代表以节点i为中心的最长回文串的半径。例如aaaa这个字符串,p[0]=p[3]=1,p[1]=p[2]=2。同时我们定义一个变量mx和id,mx代表当前判断过的回文子串最长延伸(往右)到哪里,可以理解为当前判断过的回文子串到达的最大下标。而id代表当前更新的mx值所对应的回文串的中心的下标。如果不是很清楚可以直接看下代码。剩下部分直接看代码注释吧。最好自己在纸上画一下
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); using namespace std; typedef long long ll; typedef pair<int,int> pii; const int maxn=2e5+10;//注意开两倍大小 const int INF=0x3f3f3f3f; char s[maxn],str[maxn];//s为原字符串,str为处理后的字符串 int p[maxn];//p[i]代表以i为中心的回文串的最长半径 int len1,len2;//分别代表s的长度和str的长度 void init()//预处理出str { len1=strlen(s); str[0]='$';//在首位加一个特殊字符,防止越界 str[1]='#'; for (int i=0;i<len1;i++) { str[2*i+2]=s[i]; str[2*i+3]='#'; } len2=len1*2+2; str[len2]='\0';//不能和首位字符一样 } void manacher() { int mx=0,id=0; for (int i=1;i<len2;i++) { if (mx>i) //如果当前的i在mx以内,说明i为我们之前求的回文串的范围里面
p[i]=min(p[2*id-i],mx-i);//2*id-i代表i关于id对称的下标,我们前面求出他是回文串,因此具备了对称性,而2*id-i一定小于i,我们的i是从左到右扫描的,所以p[2*id-i]一定处理出来了,所以我们可以直接用p[2*id-i]的值,但是他不能超过mx-i,因为超过mx值我们就不能保证它是回文串,所以取二者最小 else p[i]=1;//如果i不在回文串的范围内,则初始化p[i]=1; for (;str[i-p[i]]==str[i+p[i]];p[i]++);//暴力往两边匹配,找到以i为中心的最长回文串 if (mx<i+p[i])更新mx值和id值 { mx=i+p[i]; id=i; } } } int solve() { int ans=0; for (int i=1;i<len2;i++) if (ans<p[i]) ans=p[i];//找最大的p[i]就是答案 return ans-1; } int main() { while (~scanf("%s",s)) { init(); manacher(); printf("%d\n",solve()); } return 0; }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

浙公网安备 33010602011771号