【CSP膜你赛】柠檬的密码(manacher 二分 单调性 st表)

题目描述

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[100005],st[100005][20],bw[100005],lg[100005];char ch[100005];
int que(int l,int r){return l<=r?max(st[l][lg[r-l+1]],st[r-(1<<lg[r-l+1])+1][lg[r-l+1]]):0;}
int main()
{
    scanf("%d%s",&n,ch+1);bw[0]=1;
    for(int i=2;i<=n+1;i++)lg[i]=lg[i/2]+1;
    for(int i=1,r=0,mid=0;i<=n;bw[i]=bw[i-1]*29,i++)
    {
        st[i][0]=r>i?min(st[mid*2-i][0],r-i):1;
        while(ch[i-st[i][0]]==ch[i+st[i][0]])st[i][0]++;
        if(r<i+st[i][0])r=i+st[i][0],mid=i;ha[i]=(ch[i]-‘a‘+1)+ha[i-1]*29;
    }
    for(int k=1;k<=19;k++)for(int i=1;i<=n;i++)st[i][k]=max(st[i][k-1],i+(1<<(k-1))<=n?st[i+(1<<(k-1))][k-1]:0);
    for(int i=n,j=1,tmp=0;i>=1;i--)
    {
        tmp=ch[i]-‘a‘+1+tmp*29;
        while((j<n-i+1||ha[j]-ha[j-(n-i+1)]*bw[n-i+1]!=tmp)&&j<=n)j++;
        int l=0,r=(i-j)/2,mid,ret;
        while(l<=r&&j<=i)
        {
            mid=(l+r)>>1;
            if(que(j+mid,i-mid)>=mid)ret=mid,l=mid+1;
            else r=mid-1;
        }
        if(j<=i)ans=max((n-i+1)*2+ret*2-1,ans);
    }
    printf("%d\n",ans);
}

原文地址:https://www.cnblogs.com/firecrazy/p/11797639.html

时间: 2024-08-29 02:19:32

【CSP膜你赛】柠檬的密码(manacher 二分 单调性 st表)的相关文章

【CSP膜你赛】大逃亡

题目描述 魔王撒旦为了建立魔物的乐土,率领亚多拉玛雷克.艾谢尔.路西菲尔.以 及马纳果达这四位恶魔大元帅进攻人类世界.然而此时手持圣剑的勇者艾米莉亚出现了.  战败的魔王逃跑时穿越到了地球,以真奥贞夫的身份过着打工族的生活.最近真奥贞夫手头有点紧,他接到一个待遇不错的任务,但是却没有时间.无奈之下,他只能找到可靠的 YxuanwKeith 来帮忙. 然而王导最近忙于筹拍宣传片,抽不出时间,于是 YxuanwKeith 又找到了你来替他完成这个工作.YxuanwKeith 帮忙的工作是为一个大赛设

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&amp;#39;s problem(manacher+二分/枚举)

pid=5371">HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分同样,第一部分与第二部分对称. 如今给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法.求出以第i个点为中心的回文串长度.记录到数组p中 要满足题目所要求的内容.须要使得两个相邻的回文串,共享中间的一部分,也就是说.左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也

codeforces #30E Tricky and Clever Password KMP+Manacher+二分

题目大意:给定一个字符串S,要求分成A+prefix+B+middle+C+suffix6段,满足: |prefix|=|suffix| |middle|为奇数 prefix+middle+suffix为回文串 除middle外所有段长度都可以为0 要求最大化|prefix|+|middle|+|suffix|,输出一组方案(|prefix|=|suffix|=0时只输出middle) 首先我们发现suffix串是顶着右端点的,因此我们可以枚举|suffix| 对于每个|suffix|我们需要求

HDU 4864 Task (贪心+STL多集(二分)+邻接表存储)(杭电多校训练赛第一场1004)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4864 解题报告:有n台机器用来完成m个任务,每个任务有一个难度值和一个需要完成的时间,每台机器有一个可以工作的最长时间和一个可以完成的任务的难度的最大值, 一台机器能完成一个任务的条件是这台机器的最长工作时间和能完成任务的难度值必须都大于等于这个任务,而且一台机器最多完成一个任务,假设一个任务的时间为t,难度值为x,那么完成这个任务可以赚到的钱 money = 500 * t + 2 * x; 现在

CVE-2019-18988 teamviewer将用户名密码硬编码至注册表中

CVE-2019-18988 teamviewer将用户名密码硬编码至注册表中 TeamViewer stored user passwords encrypted with AES-128-CBC with they key of 0602000000a400005253413100040000 and iv of 0100010067244F436E6762F25EA8D704 in the Windows registry. If the password is reused anywhe

【BZOJ3325】[Scoi2013]密码 Manacher

[BZOJ3325][Scoi2013]密码 Description Fish是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进.通过翻阅古籍,Fish 得知了这个密码的相关信息: 1. 该密码的长度为N. 2. 密码仅含小写字母. 3. 以每一个字符为中心的最长回文串长度. 4. 以每两个相邻字符的间隙为中心的最长回文串长度. 很快Fish 发现可能有无数种满足条件的密码.经过分析,他觉得这些密码中字典序最小的一个最有可能是

2017sc 膜你赛9 比赛笔记

START 这次比赛,应该是倒数第二次吧--[总测试不要来啦~] 一开始看到P1,乍一看没懂.转看P2,发现就是组合数.然而题目数据量很大,怎么办?我想到了公式变形,C(n,m)=A(n,m) /m!=n*(n-1)*--*(n-m+1)/m!.可是题目中需要模,然而除法并不满足模的性质.我想了一会,止步不前了.当时以为正解很复杂,于是先打了个暴力组合数了事,期望得分70.再看T3,迷死我了,不理解. 我又转回去看T1.不得不说我的理解能力太弱?思维不够敏捷?居然看了半天才看懂.画了会样例,发现

1111-1112膜你赛

光棍节快乐,祝天下每只single dog单身到永远: Day1: T1:string 现在给一个字符串,你要做的就是当这个字符串中存在两个挨着的字符是相同的时就将这两个字符消除.需要注意的是,当把这两个字符消除后,可能又产生一对新的挨着的字符是相同的.比如,初始的字符串是abcddc,dd是两个挨着的相同的字符,当把"dd"消除后,得到的字符串是abcc,这时cc又是两个挨着的相同的字符,所以又应该把cc消除.重复以上操作直到剩下的串中不存在两个挨着的字符是相同的为止,输出最终剩下的