输入数据第一行包含一个正整数 N ,表示字符串的长度。
数据第二行包含一个长度为 N 的字符串,仅由小写字母组成,表示需要破译的字符串。
题目描述
输入格式
输出格式
输出数据仅包含一个整数,表示最长可能的密码的长度。
样例数据 1
备注
【样例说明】
最长的可能的密码是“abcdefgfedcba”,长度为 13 。
Lemon 选择的 x=4
Left="abcd"
Right="dcba"
Mid="efgfe"
A="orz"
B="xyz"
C="qwerty"
【数据范围】
对于 20% 的数据,满足N<=20
对于 40% 的数据,满足N<=300
对于 60% 的数据,满足N<=2000
对于 100% 的数据,满足N<=100000
解析:
首先对于中间的回文串,取最长一定是最优的
所以先用manacher预处理以每个点为中心的最长回文O(n)
最后一段的翻转==开头段
又因为C可能为空,所以用kmp预处理每个点为最后一段开头的最长匹配v【i】
因为我们只用最优解,不需要一定以 i 结尾,所以f【i】=max(f【i-1】,v【i】)
到此,全部预处理出来了,剩下只需要枚举中心算ans了
时间复杂度O(N)
代码
//manacher + KMP
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int n;
char c[100010];
int p[100010]; //manacher
char r[100010]; //l m r
int next[100010];
int f[100010]; //kmp
int main()
{
//freopen("password.in","r",stdin);
//freopen("password.out","w",stdout);
int i,j,k,s,t;
scanf("%d\n",&n);
scanf("%s",c+1); //从1开始存
int id=0;
int mx=0; //最边境+1
for(i=1;i<=n;i++)
{
if(mx>i)
p[i]=min(p[id*2-i],mx-i); //对称点与边境
else
p[i]=1;
while(c[i-p[i]]==c[i+p[i]]&&i-p[i]>0) p[i]++;
if(i+p[i]>mx)
{
mx=i+p[i];
id=i;
}
}
int ans=0;
k=0;
for(j=n;j>=1;j--)
{
k++;
r[k]=c[j];
}
next[0]=-1; //控制搜到root的next
for(j=1;j<=k;j++)
{next[j]=0;}
for(j=2;j<=k;j++)
{
for(s=next[j-1];s>=0;s=next[s])
{
if(r[j]==r[s+1]) {next[j]=s+1; break;}
}
}
int v=0;
int maxx=0;
for(j=1;j<=n;j++)
{
while(c[j]!=r[v+1]&&(v>0))
v=next[v];
if(c[j]==r[v+1]) v++;
f[j]=max(v,f[j-1]);
if(v==n) break;
}
int dd;
v=0;
for(i=2;i<n;i++) //枚举mid
{
dd=n-(i+p[i])+1;
v=min(dd,f[i-p[i]]);
if((p[i]*2-1+v*2)>ans) ans=p[i]*2-1+v*2;
}
cout<<ans;
return 0;
}