题目:求一个给定字符串的最长回文子串
[2022重新编辑]
核心思路:根据回文串的对称性剪枝
- 关于字符串长度奇偶性问题:
考虑到,如果回文串长度是奇数是偶数 所需要的操作不同,处理起来比较麻烦。
所以我们对原串做点手脚,即把每两个字符之间和头尾都加一个’#’
最后算出回文串长度后再将加上的’#'减去即可 - 数组定义:
len[i]表示以 i 为中心的回文串的最长长度的一半,即 子串半径
我们随时记录与更新p与p0,p表示已判断出过的所有回文串的最右端点位置,p0表示这个以p为最右端点的回文子串的中心点。 - 状态转移:
当我们要求len[i]时,有以下三种情况
情况1:i 在 p 的右面,没什么可优化的,只能以i为中心向两边扩散距离,计算最长长度
( 记 j 为 i 关于 po 的对称,即 j=po-(i-po) )
情况2:i 在 p左面, 且 以 po为中心的回文串 完全包含了以 j 为中心的回文子串,即 po-len[po]<=j-len[j] 这时,根据回文串对称性,状态转移,len[i]=len[j]
情况3:i 在p左面,但 以j为中心的回文子串的左端点超过了po回文串的左端点,即 po-len[po]>j-len[j],根据对称性,未超出的部分可以免去判断,只需判断超出部分。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#if 0
Writer: Goes && G.S.M.
Just a game
Enjoy it
#endif
inline char read(){
char ch=getchar();
while(ch<'a'||ch>'z') ch=getchar();
return ch;
}
char s[10000005],ch;
int len[10000005],po,p,ans;
int main()
{
//读入与预处理
int l=0;ch=read();
s[++l]='#';s[0]='$';
while(ch>='a'&&ch<='z')
s[++l]=ch,s[++l]='#',ch=getchar();
s[++l]='\0';
//状态更新
for(int i=0;i<l;i++){
if(p>i) len[i]=min(len[po*2-i],p-i);//完全包含和不完全包含的两种情况
else len[i]=1;//i在右面
while(s[i+len[i]]==s[i-len[i]]) len[i]++;//不包含部分,暴力判断
if(i+len[i]>p) po=i,p=len[i]+i;//更新p与po
}
for(int i=0;i<l;i++)
ans=max(ans,len[i]);
cout<<ans-1<<endl;
return 0;
}