P9922 题解

· · 题解

题目分析

比较有趣的题目。

猜结论很简单:根据数据范围容易想到会出现循环节,于是用哈希和 \text{map} 暴力求字符串,很快就能找到出现循环的位置并通过本题。

不过我们需要一个更严谨的说法()于是下面给出简要的分析证明。

代码

数据卡了自然溢出,需要使用双模哈希。

/*
  author: honglan0301
  Sexy_goodier _ xiaoqing
 */
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define int long long
#define mod1 998244353
#define mod2 1000000007
#define B 13331

int n,k,a,b,mx[3000005],cnt[1000005][27],cntd;
char s[3000005];
int cf1[1000005],hs1[3000005],cf2[1000005],hs2[3000005];
unordered_map <int,int> bh,cx;

int geths1(int l,int r)
{
    return (hs1[r]-hs1[l-1]*cf1[r-l+1]%mod1+mod1)%mod1;
}
int geths2(int l,int r)
{
    return (hs2[r]-hs2[l-1]*cf2[r-l+1]%mod2+mod2)%mod2;
}

signed main()
{
    //freopen("P9922_12.in","r",stdin);
    cin>>n>>k>>a>>b>>s;
    for(int i=0;i<=n;i++) mx[i]=1;
    cf1[0]=1; for(int i=1;i<=n;i++) cf1[i]=cf1[i-1]*B%mod1;
    cf2[0]=1; for(int i=1;i<=n;i++) cf2[i]=cf2[i-1]*B%mod2;
    for(int i=1;i<=n;i++) hs1[i]=(hs1[i-1]*B+s[i-1])%mod1;
    for(int i=1;i<=n;i++) hs2[i]=(hs2[i-1]*B+s[i-1])%mod2;
    for(int i=k;i<n;i++)
    {
        int nhs=geths1(i-k+1,i)*1000000007+geths2(i-k+1,i); if(!bh.count(nhs)) bh[nhs]=++cntd;
        int nr=bh[nhs]; cnt[nr][s[i]-'a'+1]++; 
        if(cnt[nr][s[i]-'a'+1]>cnt[nr][mx[nr]]||cnt[nr][s[i]-'a'+1]==cnt[nr][mx[nr]]&&s[i]-'a'+1<mx[nr]) mx[nr]=s[i]-'a'+1;
    }
    for(int i=n+1;i<=3*n;i++)
    {
        int nhs=geths1(i-k,i-1)*1000000007+geths2(i-k,i-1); int nr=bh[nhs]; 
        s[i-1]=(char)(mx[nr]+'a'-1); hs1[i]=(hs1[i-1]*B+s[i-1])%mod1; hs2[i]=(hs2[i-1]*B+s[i-1])%mod2;
        if(cx[nhs])
        {
            int wz=cx[nhs]; int cd=i-wz;
            //cout<<wz<<" "<<i<<endl; return 0;
            for(int j=a;j<=b;j++)
            {
                if(j<=i) cout<<s[j-1];
                else cout<<s[wz+(j-wz)%cd-1];
            }
            cout<<endl; return 0;
        }
        else cx[nhs]=i;
    }
}