【NOIP模拟赛】柠檬的密码(KMP/二分哈希+manachar)

该博客介绍了如何破解一个由回文串和无意义字符混淆的密码。通过分析,确定了两种策略:一是先枚举回文串再找公共前后缀,二是先枚举两边长度再求回文串。文章讨论了这两种方法,包括使用Manacher算法计算回文串长度,以及KMP算法寻找最长公共前后缀。最后,提供了两种思路的代码实现。
摘要由CSDN通过智能技术生成

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的字符串,仅由小写字母组成,表示需要破译的字符串。
输出数据仅包含一个整数,表示最长可能的密码的长度。
对于20%的数据,满足N<=20
对于40%的数据,满足N<=300
对于60%的数据,满足N<=2000
对于100%的数据,满足N<=100000

用manachar算法求出每个字符为中心往两边拓展为回文串的最长长度。
这时有两种思路。
1.先枚举回文串再求两边的长度。
因为回文串变长,两端也只是缩小同样的长度,答案一定不会变劣。
所以我们只需要在极长回文串外求一个最长公共前后缀(KMP求出)即可。
2.先枚举两边的长度再求回文串的长度。
发现随着两边长度的变长,匹配的左边的部分会一直单调往右移,于是可以使用哈希 O ( n ) O(n) O(n)维护,之后就是求出区间最长回文串,可以二分答案后缩小边界再在缩小后的区间中求manachar求出的最长长度的最大值是否大于二分的答案。

第一种思路的代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<map>
 
#define maxn 100005
#define INF 0x3f3f3f3f
 
using namespace std;
 
inline long long getint()
{
   
    long long num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    return num*flag;
}
 
int n;
char s[maxn],s1[maxn];
int p[maxn];
int nxt[maxn],f[maxn];
 
inline void manachar()
{
   
    int mx=0,id=0;
    for(int i=1;i<=n;i++)
    {
   
        if(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值