B. Password(字符串哈希+二分)

https://codeforces.com/problemset/problem/126/B

题意翻译

Asterix,Obelix和他们的临时伙伴Suffix、Prefix已经最终找到了和谐寺。然而和谐寺大门紧闭,就连Obelix的运气也没好到能打开它。

不久他们发现了一个字符串S(|S|<=1000000),刻在和谐寺大门下面的岩石上。Asterix猜想那一定是打开寺庙大门的密码,于是就大声将字符串朗读了出来,然而并没有什么事发生。于是Asterix又猜想密码一定是字符串S的子串T。

Prefix认为T是S的前缀,Suffix认为T是S的后缀,Obelix却认为T应该是S中的某一部分,也就是说,T既不是S的前缀,也不是S的后缀。

Asterix选择子串T来满足所有伙伴们的想法。同时,在所有可以被接受的子串变形中,Asterix选择了最长的一个(因为Asterix喜欢长的字符串)当Asterix大声读出子串T时,寺庙的大门开了。 (也就是说,你需要找到既是S的前缀又是S的后缀同时又在S中间出现过的最长子串)

现在给你字符串S,你需要找到满足上述要求的子串T。

输入格式:

一个长度在[1,1000000]间的只包含小写字母的字符串S。

输出格式:

输出子串T,如果T不存在,输出 "Just a legend",不包含引号。


思路:

对于一个前缀 ,如果存在一个不在最左边也不在最右边的子串 ,那么一个比当前短的前缀也一定能找到对应的子串。

所以我们先预处理找出所有合法的前缀 (合法:有对应的后缀),然后二分判当前长度在中间段是否存在满足的子串,满足的话继续延长(l=mid)

启发:一些字符串题可以利用长串满足短串一定满足的性质去思考

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e6+100;
typedef long long LL;
const LL mod=998244353;
const LL p=131;
LL f[maxn],h[maxn];
LL a[maxn],tot=0;
LL n;
string s=" ";
LL cal(LL l,LL r){
    return (f[r]%mod-f[l-1]*h[r-l+1]%mod+mod)%mod;
}
bool check(LL len,LL val){
    for(LL i=2;i+len-1<n;i++){
        if(cal(i,i+len-1)==val) return true;
    }
    return false;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  h[0]=1;
  for(LL i=1;i<maxn;i++) h[i]=h[i-1]*p%mod;
  string c;cin>>c;
  s+=c;
  n=s.size()-1;
  for(LL i=1;i<=n;i++){
     f[i]=(f[i-1]*p%mod+s[i])%mod;
  }
  for(LL i=1;i<=n;i++){
    if(cal(1,i)==cal(n-i+1,n)){
        a[++tot]=i;
    }
  }
  LL l=0;LL r=tot;
  while(l<r){
    LL mid=(l+r+1)>>1;
    if(check(a[mid],cal(1,a[mid]))) l=mid;
    else r=mid-1;
  }
  if(l==0){
    cout<<"Just a legend"<<endl;
  }
  else{
    for(LL i=1;i<=a[l];i++){
        cout<<s[i];
    }
    cout<<endl;
  }
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/113102681