zoukankan      html  css  js  c++  java
  • ZJNU 1397

    ZJNU 1397 - 隐藏口令

    (加强版)Luogu 1709 - [USACO5.5]隐藏口令Hidden Password

    题面

    pic


    思路

    大家都是最小表示法?我不会,我只会后缀数组了哭哭

    后缀数组(sa[i])可以表示排名为(i)​的后缀起始位置下标

    又因为题目要求字符串可循环,为了能够让后缀数组进行处理,将原串扩展一倍后跑后缀数组

    其后对于这个长度为(2n)​的串,从最高排名开始寻找(sa[i]le n)的位置,这说明这一段表示的后缀在原串中是最小的

    如果可以任意输出编号,那么找到的第一个位置的(sa[i])就可以直接输出了,但本题要求字符串相同时需要输出编号最小的

    考虑后缀数组的(height[i])​表示排名为(i)(i-1)的两个后缀的最长公共前缀长度

    假如我们找到了第一个满足(sa[i]le n)的位置(i),先记录下位置(i),然后初始化变量(pre=n),表示从(i)位置开始向后处理的过程中,连续排名的这些后缀的最长公共前缀长度,所以从(i)位置向后寻找时,每次都需要让(pre=min(pre,height[i]))更新最长公共前缀长度

    假如我们在(i)位置后面又找到了一个新的位置(j)满足(sa[j]le n),并且此时(pre=n)仍然成立,则说明(i)位置开始的口令与(j)位置开始的口令是相同的,所以更新答案为(min(sa[i],sa[j])),即最小编号;反之,如果(pre=n)​已经不成立了,那么就不需要再更新答案,此前找到的(i)已是最优解


    #include<bits/stdc++.h>
    #define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
    #define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define repp(i,a,b) for(int i=(a);i<(b);i++)
    #define per(i,a,b) for(int i=(a);i>=(b);i--)
    #define perr(i,a,b) for(int i=(a);i>(b);i--)
    #define all(a) (a).begin(),(a).end()
    #define mst(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    #define eb emplace_back
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> P;
    const int INF=0x3f3f3f3f;
    const ll LINF=0x3f3f3f3f3f3f3f3f;
    const double eps=1e-12;
    const double PI=acos(-1.0);
    const ll mod=998244353;
    const int dx[8]={0,1,0,-1,1,1,-1,-1},dy[8]={1,0,-1,0,1,-1,1,-1};
    void debug(){cerr<<'
    ';}template<typename T,typename... Args>void debug(T x,Args... args){cerr<<"[ "<<x<< " ] , ";debug(args...);}
    mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
    ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
    ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
    ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
    ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
    ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}
    
    const int N=200050;
    int xx[N],yy[N],cnt[N];
    int sa[N],rk[N],height[N];
    char str[N];
    void getSA_DA(int n,int M){
        int i,j,p,*x=xx,*y=yy;
        for(i=0;i<M;i++)cnt[i]=0;
        for(i=0;i<n;i++)cnt[x[i]=str[i]]++;
        for(i=1;i<M;i++)cnt[i]+=cnt[i-1];
        for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
        for(j=1,p=1;p<n;j<<=1,M=p){
            for(p=0,i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<M;i++)cnt[i]=0;
            for(i=0;i<n;i++)cnt[x[y[i]]]++;
            for(i=1;i<M;i++)cnt[i]+=cnt[i-1];
            for(i=n-1;i>=0;i--)sa[--cnt[x[y[i]]]]=y[i];
            for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
                x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
        }
    }
    void getHeight(int n){
        int i,j,k=0;
        for(i=1;i<=n;i++)rk[sa[i]]=i;
        for(i=0;i<n;height[rk[i++]]=k)
            for(k?k--:0,j=sa[rk[i]-1];str[i+k]==str[j+k];k++);
        for(i=n;i;i--)rk[i]=rk[i-1],sa[i]++;
    }
    
    void solve()
    {
        int n;
        string s,t;
        cin>>n;
        while(cin>>t)
            s+=t;
        s+=s;
        repp(i,0,n+n)
            str[i]=s[i];
        
        getSA_DA(n+n+1,128);
        getHeight(n+n);
        
        int ans=-1,pre=-1;
        
        rep(i,1,n+n) //此时长度为2n
        {
            pre=min(pre,height[i]); //更新pre
            if(sa[i]<=n) //假如找到合法串
            {
                if(ans==-1) //此前没找到过答案,则该位置是暂时的最优解
                {
                    ans=sa[i]-1;
                    pre=n; //定此时最长公共前缀为n
                }
                else
                {
                    if(pre==n) //最长公共前缀不变,编号取小
                        ans=min(ans,sa[i]-1);
                    else if(pre<n) //已经变了的话则直接结束寻找即可
                        break;
                }
            }
        }
        
        cout<<ans<<'
    ';
    }
    int main()
    {
        closeSync;
        //multiCase
        {
            solve();
        }
        return 0;
    }
    

  • 相关阅读:
    Markdown编辑器语法指南2
    python文件读写的学习
    K:图相关的最小生成树(MST)
    K:哈弗曼树
    K:树、二叉树与森林之间的转换及其相关代码实现
    K:二叉树
    K:树与二叉树
    KandQ:那年,那树,那些知识点
    K:java中的安全模型(沙箱机制)
    K:java中的RMI(Remote Method Invocation)
  • 原文地址:https://www.cnblogs.com/stelayuri/p/15149867.html
Copyright © 2011-2022 走看看