題目描述
輸入格式
輸入數據第一行包含一個正整數 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;
}