大一第十四周训练A题 最长回文串
马拉车算法
题目:
给出一个只由小写英文字符a,b,c…y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
输入:
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c…y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
输出:
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
样例:
输入:
aaaa
abab
输出:
4
3
——————————————————————————————————————
马拉车算法:
原本求回文串时要分奇偶性讨论,马拉车是通过在每两个相邻的字符之间插入分隔符,这样新字符串的长度一定为奇数:
比如设原来字符串的长度为l,新字符串的长度为ll;
则可知插入的分隔符一共有l+1个,所以ll=2*l+1,一定为奇数。
然后开一个数组记录以第i个字符为中心的最长回文串的右端点到点i的长度。
那如何进行计算呢?看下面:
先设p代表当前位置时之前的所有的回文子串所能到达的最右边的那个子串的右端点;
po代表这个子串的位置。
接下来分两种情况讨论:
(1)i<p
设i关于点po的对称点为j;如果子串j在po的内部,则子串i的长度最小等于j的长度(因为po也是一个回文串,i和j都在po的内部,且一左一右,所以显然可知上面的结论);
(2)i>=p
这时候i就在po的外部,无法根据对称性取巧,所以就以i为中心一个一个字符的找。
最后,为了防止越界的问题,就在字符串的最前面加一个未出现过的字符,这样就没问题了。
——————————————————————————————————————
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char a[110010];
char b[300010];//插入分隔符号
int c[300010];//记录以点i为中心的回文串的半径
int main()
{
int i,j,l,p,po,flag,maxx;
while(~scanf("%s",a))
{
b[0]='$';//防止越界
i=0;
l=1;
while(a[i]!='\0')//向两个字符之间插入分隔符号
{
b[l++]='#';
b[l++]=a[i];
i++;
}
b[l]='#';
/*这样新字符串长度一定为奇数;
比如设原来字符串长度为l,新字符串长度为ll;
则可知插入的分隔符一共有l+1个,
所以ll=2*l+1,一定为奇数。
*/
memset(c,0,sizeof(c));
p=po=0;
/*
p表示当前位置时之前的所有的回文子串所能
到达的最右边的那个子串的右端点;
po表示这个最长回文子串的位置。
*/
for(i=0;i<=l;i++)
{
c[i]=p>i?min(c[2*po-i],p-i):1;
/*
分两种情况,p>i和p<=i;
p>i直接根据回文串的定义可快速求解;
p<=i则需要一个一个字符去计算回文串长度。
*/
while(b[i+c[i]]==b[i-c[i]])
{
c[i]++;
}
if(i+c[i]>p)
{
p=i+c[i];
po=i;
}
}
maxx=0;//for循环找出最大的
for(i=0;i<=l;i++)
{
maxx=max(maxx,c[i]);
}
maxx-=1;//最后还要减一,因为ll=2*l+1。
printf("%d\n",maxx);
}
return 0;
}
//里这在佬大:https://blog.csdn.net/haut_ykc/article/details/52137496