题目描述

Lemon觉得他需要一个复杂的密码来保证他的帐号的安全。他经过多日思考,决定使用一个长度为奇数的回文串来作为他的密码。
  但是这个回文串太长了,Lemon记不住,于是Lemon决定把它记在本子上。当然直接把密码明文记录实在太愚蠢了,于是Lemon决定在记录时加入一些无意义的字符以保证密码的安全。
  具体来说,假设Lemon的密码串是S,Lemon选择了一个不超过len(S)/2的正整数x,然后把S的前x个字符组成的字符串设为Left,把S的后x个字符组成的字符串设为Right,把S其余的字符组成的字符串设为Mid.
  Lemon实际记录在密码本上的内容是A+Left+B+Mid+C+Right. 其中A,B,C都是无意义的字符串(有可能是空串)。他觉得这样就很安全了。
  某一天,Melon无意发现了Lemon的笔记本,并发现了这个字符串。Melon决定把Lemon的密码破解出来。但是显然有不计其数的可能密码。
  Melon认为,Lemon的密码一定很长,于是他想知道,这个字符串里隐藏的最长可能的密码有多长呢?

输入格式

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

输出格式

输出数据仅包含一个整数,表示最长可能的密码的长度。
输入样例
  25
  orzabcdxyzefgfeqwertydcba
输出样例
  13
提示
  最长的可能的密码是abcdefgfedcba,长度为13
  Lemon选择的x=4
  Left="abcd" Right="dcba" Mid="efgfe"
  A="orz" B="xyz" C="qwerty"
  时间限制1s
  对于20%的数据,满足N<=20
  对于40%的数据,满足N<=300
  对于60%的数据,满足N<=2000
  对于100%的数据,满足N<=100000

分析

在网上找了半天题解啥都没找到最后放弃了,去问的Master_Yi

由于Right的右端点一定是原序列中的右端点,所以我们可以去枚举Right的左端点,也可以说是Right的长度,然后用hash值去找与它对称的Left

用贪心的思想去找最左边的且与Right对称的 Left(这样可以给中间的mid留下足够的长度),从小到大枚举Left的左端点的坐标,可以发现它有单调性的

模糊感性证明

如果当前枚举的左端点形成的Left与Right不对称,之后的枚举新的Right的长度时,以这个点为左端点的Left肯定与Right不对称

所以最左边与Right对称的Left的左端点一定随着Right的长度单增(或者说不下降

于是就可以O(n)确定Left和Right,剩下中间的Mid,因为Mid一定是个回文串,可以用manacher预处理每个点的len数组,即以这个点为对称中心的最长子串长度

设中间剩下的区间为(l,r),二分一下Mid的半径,设为k,用st表检验(l+k,r-k)中是否有len数组的值大于等于k的,如果有就更新答案,然后继续搜索。

整了这么多东西,发现复杂度只有nlogn。。。。。。

Code

#include<cstdio>
#include<algorithm>
using namespace std;
int n,ans,ha[],st[][],bw[],lg[];char ch[];
int que(int l,int r){return l<=r?max(st[l][lg[r-l+]],st[r-(<<lg[r-l+])+][lg[r-l+]]):;}
int main()
{
scanf("%d%s",&n,ch+);bw[]=;
for(int i=;i<=n+;i++)lg[i]=lg[i/]+;
for(int i=,r=,mid=;i<=n;bw[i]=bw[i-]*,i++)
{
st[i][]=r>i?min(st[mid*-i][],r-i):;
while(ch[i-st[i][]]==ch[i+st[i][]])st[i][]++;
if(r<i+st[i][])r=i+st[i][],mid=i;ha[i]=(ch[i]-'a'+)+ha[i-]*;
}
for(int k=;k<=;k++)for(int i=;i<=n;i++)st[i][k]=max(st[i][k-],i+(<<(k-))<=n?st[i+(<<(k-))][k-]:);
for(int i=n,j=,tmp=;i>=;i--)
{
tmp=ch[i]-'a'++tmp*;
while((j<n-i+||ha[j]-ha[j-(n-i+)]*bw[n-i+]!=tmp)&&j<=n)j++;
int l=,r=(i-j)/,mid,ret;
while(l<=r&&j<=i)
{
mid=(l+r)>>;
if(que(j+mid,i-mid)>=mid)ret=mid,l=mid+;
else r=mid-;
}
if(j<=i)ans=max((n-i+)*+ret*-,ans);
}
printf("%d\n",ans);
}

最新文章

  1. MQTT开发笔记之《安全传输-自问自答》
  2. ExcelReport第三篇:扩展元素格式化器
  3. commonJS — 数组操作(for Array)
  4. Entity Framework(序)
  5. AirDrop显示名字的修改问题
  6. Akka Stream文档翻译:Motivation
  7. 一个tomcat部署俩个java web项目
  8. C 运算符与表达式
  9. 侯老师的话(Application Framework)
  10. 【转】Ubuntu下deb包的安装方法
  11. pragma message任务
  12. htmlUtil 网页爬取工具
  13. 使用 MapTiler 进行地图切片
  14. 062、如何使用flannel host-gw backend(2019-04-02 周二)
  15. 北航MOOC客户端
  16. jmeter参数化、添加变量、生成随机数和导入csv文件数据
  17. AngularJS实战之路由ui-view传参
  18. zTree树
  19. MVC实现有关时间的进度条,使用jQuery ui的progressbar
  20. MATLAB 人脸定位

热门文章

  1. 服务发现对比:Zookeeper vs etcd vs Consul
  2. Nginx优化配置,轻松应对高并发
  3. witchcase
  4. hello world之vivado程序解决方法
  5. 安卓、ios时间转换成时间戳的形式
  6. windows 2003 域控服务器导出全部hash的方法
  7. Java String 字符串
  8. 解决vant-weapp组件库的example的导入问题
  9. Spring Cloud注册中心Eureka设置访问权限并自定义鉴权页面
  10. 最长上生子序列LIS