superoj779 柠檬的密码

来源:互联网 发布:adobe最新软件 编辑:程序博客网 时间:2024/06/09 20:09

题目描述

输入格式

输入数据第一行包含一个正整数 N ,表示字符串的长度。 
数据第二行包含一个长度为 N 的字符串,仅由小写字母组成,表示需要破译的字符串。

输出格式

输出数据仅包含一个整数,表示最长可能的密码的长度。

样例数据 1

输入  [复制]

25 
orzabcdxyzefgfeqwertydcba

输出

13

备注

【样例说明】 
最长的可能的密码是“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];     //manacherchar r[100010];     //l m rint next[100010];int f[100010];    //kmpint 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;}



0 0