• HDU5008 Boring String Problem(k大子串,后缀系列)


    HDU-5008 Boring String Problem(k大子串,后缀系列)

    这个题似乎没有办法用后缀自动机写。。。

    \[\ \]

    后缀数组

    由于后缀数组已经排好序,所以可对于\(sa[i]\)考虑其贡献的个数为\(n-sa[i]+1-lcp[i-1]\)

    考虑个数累前缀和\(Sum[i]\),二分就\(Sum[p]\ge k\)能得知要找的串在哪个后缀\(p\)的前缀集里,但是这个后缀可能不止出现的一次

    求得的长度\(len=k-Sum[p-1]+lcp[p-1]\)就是排名再加上已经出现的

    所有包含这个串的后缀是一段连续的区间\(l,r\)满足\(\forall_{i\in [l,r]}LCP(sa[i],p)\ge len\)

    所以在\([l,r]\)中找出最小的下标即可

    #include<bits/stdc++.h>
    using namespace std;
    
    #define reg register
    typedef long long ll;
    typedef unsigned long long ull;
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    
    #define pb push_back
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
    
    char IO;
    template <class T=int> T rd(){
        T s=0;
        int f=0;
        while(!isdigit(IO=getchar())) if(IO=='-') f=1;
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
    
    const int N=2e5+10;
    
    int n;
    char s[N];
    int cnt[N],tmp[N],rk[N],sa[N],lcp[N];
    
    ll Sum[N];
    
    void PreMake(){
        memset(cnt,0,800);
        rep(i,1,n) cnt[(int)s[i]]++;
        rep(i,1,200) cnt[i]+=cnt[i-1];
        rep(i,1,n) rk[i]=cnt[(int)s[i]],sa[i]=i;
        rep(i,n+1,n*2) rk[i]=0;
        for(reg int k=1;k<=n;k<<=1) {
            rep(i,0,n) cnt[i]=0;
            rep(i,1,n) cnt[rk[i+k]]++;
            rep(i,1,n) cnt[i]+=cnt[i-1];
            drep(i,n,1) tmp[cnt[rk[i+k]]--]=i;
            ;
            rep(i,0,n) cnt[i]=0;
            rep(i,1,n) cnt[rk[i]]++;
            rep(i,1,n) cnt[i]+=cnt[i-1];
            drep(i,n,1) sa[cnt[rk[tmp[i]]]--]=tmp[i];
            ;
            rep(i,1,n) tmp[sa[i]]=tmp[sa[i-1]]+(rk[sa[i]]!=rk[sa[i-1]]||rk[sa[i]+k]!=rk[sa[i-1]+k]);
            rep(i,1,n) rk[i]=tmp[i];
        }
        rep(i,1,n) lcp[i]=0;
        for(reg int i=1,h=0;i<=n;++i) {
            if(h) h--;
            int j=sa[rk[i]-1];
            while(i+h<=n && j+h<=n && s[i+h]==s[j+h]) h++;
            lcp[rk[i]-1]=h;
        }
        rep(i,1,n) Sum[i]=Sum[i-1]+n-sa[i]+1-lcp[i-1];
    }
    
    int a[20][N],b[20][N],Log[N];
    
    int main(){
        rep(i,2,N-1) Log[i]=Log[i>>1]+1;
        while(~scanf("%s",s+1)) {
            n=strlen(s+1);
            PreMake();
            rep(i,1,n) a[0][i]=lcp[i],b[0][i]=sa[i];
            rep(i,1,17) {
                int len=(1<<(i-1));
                rep(j,1,n) a[i][j]=min(a[i-1][j],a[i-1][j+len]),b[i][j]=min(b[i-1][j],b[i-1][j+len]);
            }
            int a=0,b=0;
            rep(i,1,rd()) {
                ull x=(rd<ull>()^a^b)+1;
                int p=lower_bound(Sum+1,Sum+n+1,x)-Sum;
                if(p>n) a=b=0;
                else {
                    int l=p,r=p,len=x-Sum[p-1]+lcp[p-1];
                    drep(j,17,0) {
                        if(l-(1<<j)>0 && ::a[j][l-(1<<j)]>=len) l-=(1<<j);
                        if(r+(1<<j)<=n && ::a[j][r]>=len) r+=(1<<j);
                    } // 倍增找到所有满足条件的后缀
                    int d=Log[r-l+1],res=min(::b[d][l],::b[d][r-(1<<d)+1]); // ST表查询最小起始下标
                    a=res,b=a+len-1;
                }
                printf("%d %d\n",a,b);
            }
        }
    }
    

    \[\ \]

    \[\ \]

    后缀树

    建树之后直接按照字典序遍历得到即可

    最终起始和后缀数组是一样的

    #include<bits/stdc++.h>
    using namespace std;
    
    #define reg register
    typedef long long ll;
    typedef unsigned long long ull;
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    
    #define pb push_back
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
    
    char IO;
    template <class T=int> T rd(){
        T s=0;
        int f=0;
        while(!isdigit(IO=getchar())) if(IO=='-') f=1;
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return f?-s:s;
    }
    
    const int N=2e5+10;
    
    int n;
    char s[N];
    
    int trans[N][26],link[N],len[N],stcnt,lst,End[N];
    
    struct Edge{
        int to,nxt;
    } e[N];
    int head[N],ecnt;
    void AddEdge(int u,int v) {
        e[++ecnt]=(Edge){v,head[u]};
        head[u]=ecnt;
    }
    
    void Init(){
        link[0]=-1,len[0]=0;
        rep(i,0,stcnt) {
            head[i]=0;
            rep(j,0,25) trans[i][j]=0;
        }
        lst=stcnt=ecnt=0;
    }
    
    void Extend(int c) {
        int cur=++stcnt,p=lst;
        End[cur]=len[cur]=len[p]+1;
        while(~p && !trans[p][c]) trans[p][c]=cur,p=link[p];
        if(p==-1) link[cur]=0;
        else {
            int q=trans[p][c];
            if(len[q]==len[p]+1) link[cur]=q;
            else {
                int clone=++stcnt;
                memcpy(trans[clone],trans[q],104);
                End[clone]=End[q];
                len[clone]=len[p]+1,link[clone]=link[q];
                while(~p && trans[p][c]==q) trans[p][c]=clone,p=link[p];
                link[cur]=link[q]=clone;
            }
        }
        lst=cur;
    }
    
    
    ll Sum[N];
    int Pos[N],cnt,LstL,LstR;
    
    struct Node{
        int x,c;
        bool operator < (const Node __) const {
            return c<__.c;
        }
    };
    
    void dfs(int u) {
        if(u) {
            Sum[cnt+1]=Sum[cnt]+len[u]-len[link[u]];
            Pos[++cnt]=u;
        }
        vector <Node> son;
        for(reg int i=head[u];i;i=e[i].nxt) {
            int v=e[i].to;
            son.pb((Node){v,s[n-End[v]+len[link[v]]+1]});
        }
        sort(son.begin(),son.end());// 按照字典序遍历
        rep(i,0,son.size()-1) {
            dfs(son[i].x);
            cmax(End[u],End[son[i].x]); // 处理出最前面的位置
        }
    }
    
    int main(){
        while(~scanf("%s",s+1)) {
            n=strlen(s+1);
            Init();
            drep(i,n,1) Extend(s[i]-'a'); // 倒着建SAM就是后缀树了
            rep(i,1,stcnt) AddEdge(link[i],i);
            cnt=LstL=LstR=0;
            dfs(0);
            rep(i,1,rd()) {
                ull x=(rd<ull>()^LstL^LstR)+1;
                int p=lower_bound(Sum+1,Sum+cnt+1,x)-Sum; // 二分找到出现位置
                if(p>cnt) puts("0 0"),LstL=LstR=0;
                else printf("%d %d\n",LstL=(n-End[Pos[p]]+1),LstR=(n-End[Pos[p]]+len[link[Pos[p]]]+(x-Sum[p-1])));
            }
        }
    }
    
    
    
  • 相关阅读:
    Java Web学习总结(16)——JSP的九个内置对象
    Java Web学习总结(15)——JSP指令
    【我的物联网成长记11】8招带你玩转规则引擎
    云图说|高效管理华为云SAP的“秘密武器”
    Python 中更优雅的日志记录方案
    有了它,Python编码再也不为字符集问题而发愁了!
    【鲲鹏来了】手把手教你创造一个属于自己的鲲鹏开发者环境
    解密昇腾AI处理器--DaVinci架构(计算单元)
    使用Keil5构建GD32450i-EVAL工程
    云图说|SAP技术画册“一点通”
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12213479.html
Copyright © 2020-2023  润新知