superoj779 柠檬的密码

题目描述

输入格式

输入数据第一行包含一个正整数 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];     //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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值