Codeforces 30 E
题意:
一个长度为奇数的回文串可以写成\(a+b+a'\)的形式,\(b\)的长度也是奇数,\(a'\)为\(a\)的反串。我们设\(S=x+a+y+b+z+a'\),其中\(x,y,z\)为任意可以为空的字符串。
给定\(S\),求原来回文串的最长长度,以及\(a,b,a'\)在\(S\)中的起始位置和长度。
题解:
有一个关键的贪心结论:
假设我们已经定下了\(b\)的中心点为\(i\),那么最长的原串,一定是让\(b\)的长度尽可能长,其次让\(a,a'\)的长度尽可能长。
证明:
如果有\(b\)可以去更短的串,分两种情况讨论:
- \(a,a'\)和\(b\)相邻:那么我们可以让\(b\)为整个回文串\(a+b+b'\),不影响答案。
- \(a,a'\)其中一个和\(b\)相邻、或两个都不和\(b\)相邻:那如果\(b\)不是以\(i\)为中心最长的回文串,那么我们完全可以将\(b\)向两侧各延伸一格,不会使得答案更劣。
所以,我们先求出以\(i\)为中心最长回文串,设其长度为\(2p_{i}-1\),这个可以使用manacher求出。(由于只要求长度为奇数的回文串,所以我们甚至没必要加分隔符)
然后我们枚举中心点\(i\),找到最长的\(a,a'\)。
发现\(a'\)是\(S\)的一个后缀,那么我们就可以将模式串\(S\)和匹配串\(S'\)进行KMP,得到\(f(i)\)表示以\(i\)结尾\(S\)的前缀 的后缀,最多能匹配多少\(S'\)的前缀。
那么我们求得的\(f(i)\)就是\(a\)以\(i\)结尾的最大长度。
所以为了求\(\leq i-p_{i}\)的\(f(i)\)的min,我们可以使用前缀max。注意\(a'\)也不能和\(p\)相交,所以我们还要求从在\(a\)最大时\(a'\)的起始位置,和\(i+p_{i}\)取max,才是真正的长度。
时间复杂度为\(O(n)\)。
代码:
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
const int maxn=100005;
int n,ans,x,y,p[maxn],nxt[maxn],f[maxn],id[maxn];
char str[maxn],istr[maxn];
void manacher() {
int mid=0,mr=0;
for(int i=1;i<=n;i++) {
if(i<=mr) p[i]=std::min(p[mid+mid-i],mr-i+1);
else p[i]=1;
while(i+p[i]<=n&&i-p[i]>=1&&str[i+p[i]]==str[i-p[i]]) p[i]++;
if(i+p[i]-1>mr) mr=i+p[i]-1,mid=i;
}
}
void kmp() {
for(int i=2,j=0;i<=n;i++) {
while(j&&istr[j+1]!=istr[i]) j=nxt[j];
if(istr[j+1]==istr[i]) j++;
nxt[i]=j;
}
for(int i=1,j=0;i<=n;i++) {
while(j&&istr[j+1]!=str[i]) j=nxt[j];
if(istr[j+1]==str[i]) j++;
f[i]=j;
if(j==n) j=nxt[j];
}
}
int main() {
scanf("%s",str+1); n=strlen(str+1);
for(int i=1;i<=n;i++) istr[i]=str[n-i+1];
manacher(); kmp();
for(int i=1;i<=n;i++)
if(f[i]>f[i-1]) id[i]=i;
else id[i]=id[i-1],f[i]=f[i-1];
for(int i=1;i<=n;i++) {
int longest=f[i-p[i]];
longest=std::min(longest,n-(i+p[i]-1));
int trans=2*longest+2*p[i]-1;
if(trans>ans) {
ans=trans;
x=id[i-p[i]],y=i;
}
}
std::pair<int,int> s1(x-f[x]+1,x),s2(y-p[y]+1,y+p[y]-1),s3(n-f[x]+1,n);
if(s1.first<=s1.second) {
printf("3\n%d %d\n%d %d\n%d %d\n",s1.first,s1.second-s1.first+1,
s2.first,s2.second-s2.first+1,s3.first,s3.second-s3.first+1);
} else {
printf("1\n%d %d\n",s2.first,s2.second-s2.first+1);
}
return 0;
}
浙公网安备 33010602011771号