其他分享
首页 > 其他分享> > [CF126B]Password 多解

[CF126B]Password 多解

作者:互联网

/*法1:通过 exkmp,求得 z 数组
错误复杂度方法:
    1.枚举每一个后缀,通过 Z 可以求出与前缀的 LCP 然后去串里面暴力找子串。
    2.枚举每一个中间串,通过 Z 可以求出与前缀的 LCP ,将这些长度暴力染色,遇到后缀就判一下。
正解:
    通过exkmp[i]+i==len判断出这个串既是前缀又是后缀
    maxn记录当前最大的exkmp,如果说我们已经找到了一个后缀前缀,
    并且在i前面的最大的exkmp[i]是要比n-i长的,说明在中间至少也出现了一个相同的子串
*/
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000005;

char s[N];
int exkmp[N],len;

void Exkmp(){
    exkmp[1]=len;
    int l=0,r=0;
    for(int i=2;i<=len;i++){
        if(i<=r)exkmp[i]=min(exkmp[i-l+1],r-i+1);
        while(s[i+exkmp[i]]==s[exkmp[i]+1]&&i+exkmp[i]<=len)
            exkmp[i]++;
        if(i+exkmp[i]-1>r)r=i+exkmp[i]-1,l=i;
    }
}

int maxn=-10,pos;
int main(){
    scanf("%s",s+1);
    len=strlen(s+1);
    Exkmp();
    for(int i=2;i<=len;i++){
        if(i+exkmp[i]-1==len&&maxn>=len-i+1){pos=i;break;}
        maxn=max(maxn,exkmp[i]);
    }
    if(!pos)puts("Just a legend");
    else for(int i=1;i<=len-pos+1;i++)printf("%c",s[i]);
    return 0;
}/*
法2:kmp
找一个既是S的前缀又是S的后缀同时又在S中间出现过的最长子串
其实就是找一个从0开始的一个子串使得它的nxt>=nxt[len]
从2枚举到n-1这样可以保证既不是前缀也不是后缀,
如果next[i]=next[n]那么就找到了最大的子串
注意如果 next[n]>max(next[1...n-1]) 时显然无法找到
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+6;

char a[N];
int kmp[N],n,maxx=0;

void getkmp(){
    kmp[1]=0;
    for(int i=2,j=0;i<=n;i++){
        while(a[i]!=a[j+1]&&j)j=kmp[j];
        if(a[i]==a[j+1])j++;
        kmp[i]=j;
        if(i!=n)maxx=max(maxx,kmp[i]);
    }
}

int main(){
    scanf("%s",a+1);
    n=strlen(a+1);
    getkmp();
    int x=kmp[n];
    if(!x)puts("Just a legend");
    else{
        while(x>maxx)x=kmp[x];
        if(!x)puts("Just a legend");
        for(int i=2;i<n;i++){
            if(x==kmp[i]){
                for(int j=1;j<=kmp[i];j++)
                    printf("%c",a[i]);
                puts("");
                return 0;
            }
        }
    }
} 
*//*
法3:字符串hash神教
思路:字符串,于是想到hash。发现时间复杂度有问题,考虑优化
    优化字符串方式在于合理利用残余的信息,如kmp
    发现二分性质,解决
性质:对于一个前缀 T,如果存在一个不在最左边也不在最右边的子串 U,
    那么一个比 T 短的前缀也一定能找到对应的 U'
    这条性质为二分奠定了基础
做法:1.O(n) 找出所有合法的前缀 T(有对应的后缀),
    2.二分,判断是否存在 U O(n) 判,总复杂度 O(nlogn)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+6;
const int mod = 998244353

char s[N];
int hsh[N],p[N],n;
vector<int>v;

int calc(int l,int r){return hsh[r]-hsh[l-1]*p[r-l+1];}
bool check(int len,int val){
    for(int i=2;i+len-1<n;i++){
        if(val==calc(i,i+len-1))
            return ture;
    }
    return false;
}

int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    p[0]=1;
    for(int i=1;i<=n;i++)
        p[i]=p[i-1]*131%mod,
        hsh[i]=(hsh[i-1]*131%mod+s[i])%mod;
    for(int i=1;i<=n-2;i++){
        if(calc(1,i)==calc(n-i+1,n))
            v.push_back(i);
    }
    int l=0,r=(int)v.size()-1,ans=0,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(v[mid],calc(1,v[mid])))
            ans=mid,l=mid+1;
        else r=mid-1;
    }
    if(!ans)puts("Just a legend");
    else for(int i=1;i<=ans;i++)printf("%c",s[i]);
    return 0;
}
*//*
法4:正反两次kmp
我们可以发现一个切入点:
对于我们找到的一个满足条件的字符串 P(假设它在S中的起始和终点坐标分别为l,r)
那么我们发现(设 |S|=n):
P 既是 S[1...r] 的前缀,也是 S[1...r] 的后缀
P 既是 S[l...n] 的前缀,也是 S[l...n] 的后缀
具体:https://www.luogu.com.cn/blog/Sangber/solution-cf126b
*//*
法5:exkmp + hash 乱搞
exkmp求出自匹配数组,然后 hash 得到所有后缀存入map,
枚举 exkmp,map判断是否存在相应后缀
(以下是 0 分代码,还在debug)
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000005;

char s[N];
int exkmp[N],len;
ull hsh[N],p[N];
map<ull,bool>mp;
bool debug=0;

void Exkmp(){
    exkmp[1]=len;
    int l=0,r=0;
    for(int i=2;i<=len;i++){
        if(i<=r)exkmp[i]=min(exkmp[i-l+1],r-i+1);
        while(s[i+exkmp[i]]==s[exkmp[i]+1]&&i+exkmp[i]<=len)
            exkmp[i]++;
        if(i+exkmp[i]-1>r)r=i+exkmp[i]-1,l=i;
    }
}

int main(){
    scanf("%s",s+1);
    len=strlen(s+1);
    p[0]=1;
    for(int i=1;i<=len;i++){
        p[i]=p[i-1]*131;
        hsh[i]=hsh[i-1]*131+s[i];
    }
    Exkmp();
    if(debug){
        printf("exkmp:\n");
        for(int i=1;i<=len;i++)printf("i:%d exkmp:%d\n",i,exkmp[i]);
        printf("hash:\n");    
    }
    for(int i=1;i<=len;i++){
        mp[hsh[len]-hsh[i-1]*p[len-i+1]]=1;
        if(debug)printf("i:%d hsh:%llu\n",i,hsh[len]-hsh[i-1]*p[len-i+1]);
    }
    if(debug)printf("compare:\n");
    int ans=0,ansp;
    for(int i=2;i<len;i++){
        if(i+exkmp[i]-1==len||!exkmp[i])continue;
        if(mp[hsh[exkmp[i]]]&&exkmp[i]>ans)
            if(debug)printf("i:%d exkmp[i]:%d hah:%llu\n",i,exkmp[i],hsh[exkmp[i]]),
            ans=exkmp[i],ansp=i;
    }
    if(!ans)puts("Just a legend");
    else for(int i=ansp;i<ansp+ans;i++)printf("%c",s[i]);
    return 0;
}
*/

 

标签:exkmp,const,CF126B,hsh,mid,len,多解,int,Password
来源: https://www.cnblogs.com/wyb-sen/p/14355880.html