zoukankan      html  css  js  c++  java
  • 后缀数组题目

    巨佬博客: https://www.cnblogs.com/zwfymqz/p/8413523.html

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1e6+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],hight[N];
    char s[N];
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=M;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i];
    }
    
    void suffixsort() {
        M=100;
        for(int i=1;i<=n;i++) rk[i]=s[i]-'0'+1,tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++)tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void gethight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            hight[rk[i]]=k;
        }
    }
    
    int main() {
        scanf("%s",s+1);n=strlen(s+1);
        suffixsort();
        for(int i=1;i<=n;i++) printf("%d ",sa[i]);
    }
    后缀数组模板

    $sa[i]:排名为i的后缀的位置$

    $rk[i]:从第i个位置开始的后缀的排名,下文为了叙述方便,把从第i个位置开始的后缀简称为后缀i$

    $tp[i]:基数排序的第二关键字,意义与sa一样,即第二关键字排名为i的后缀的位置$

    $sum[i]:ii号元素出现了多少次。辅助基数排序$

    $s:字符串,s[i]表示字符串中第i个字符串$

    $height[i]:lcp(sa[i],sa[i−1]),即排名为i的后缀与排名为i−1的后缀的最长公共前缀$

    Long Long Message POJ - 2774

    题意:

    $一个人读取同一个字符串两次,因为机器的问题,每次读取可能会在原串的左边和右边加上一串随机字符串, 问原串的最大长度$

    题解:

    • $将两个串合并成一个串$
    • $求出height,     所以只要遍历所有的height 求出最大值即可  注意判定height所表示的第i串和第i-1串是否在两个串里$
    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    using namespace std;
    typedef long long ll;
    
    const int N=2e5+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N],lens,lens1;
    char s[N],s1[N];
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=M;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i];
    }
    
    void suffixsort() {
        M=100;
        for(int i=1;i<=n;i++) rk[i]=s[i]-'0'+1,tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++)tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    bool ok(int x,int y) {
        if(x>y)swap(x,y);
        return x>=1&&x<=lens&&y>=lens+1&&y<=lens+lens1;
    }
    
    int main() {
        scanf("%s",s+1); lens=strlen(s+1);
        scanf("%s",s1+1); lens1=strlen(s1+1);
        strcat(s+1,s1+1);
        n=lens+lens1;
        suffixsort();getheight();int ans=0;
    
        for(int i=2;i<=lens+lens1;i++)
            if(ok(sa[i],sa[i-1]))ans=max(ans,height[i]);
        cout<<ans;
    }
    View Code

    牛奶模式Milk Patterns P2852 

    题意:

    $求出给定串的重复 k 次的重复子串,使之长度最大化 $

    题解:

    • 和上面那题非常类似  只要选k个后缀 求其最大公共前缀长度 ,并使之最大化即可
    • 用单调栈跑height数组即可 单调栈维护长度为k-1的最小值
    • 注意最好离散化 不然基数排序容易T
    • 求相同子串肯定是和height有关
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=2e5+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N],lens,lens1;
    int s[N],b[N],st[N],k,ans=0;
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=M;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i];
    }
    
    void suffixsort() {
        for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++)tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    int main() {
        cin>>n>>k;k--;
        for(int i=1;i<=n;i++)scanf("%d",&s[i]),b[i]=s[i];
        sort(b+1,b+1+n);
        int nn=unique(b+1,b+1+n)-b-1;M=nn;
        for(int i=1;i<=n;i++)s[i]=lower_bound(b+1,b+1+nn,s[i])-b;
        suffixsort();getheight();
        int l=1,r=0;
        for(int i=2;i<=n;i++) {
            if(l<=r&&st[l]<i-k+1)l++;
            while(l<=r&&height[st[r]]>height[i])r--;
            st[++r]=i;
            ans=max(ans,height[st[l]]);
        }
        cout<<ans;
    }
    View Code

    [POI2000]公共串 P5546 

    题意:

    求n个串的最长公共子串

    题解:

    • 之前做了两个子串的最长公共子串  只要合并起来扫一遍就行了  但是多个子串显然不能直接扫
    • 首先同样将所有串接在一起  中间用特殊符号隔开 
    • 直接二分最长公共子串的长度即可  然后扫height数组 统计cnt(来自不同的串的个数)等于n的时候就满足条件
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=3e5+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N],lens,lens1;
    char s1[N];
    int T,s[N],belong[N],ncnt=1,vis[N];
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=M;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[ sum[rk[tp[i]]]-- ]=tp[i];
    }
    
    void suffixsort() {
        for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
        M=40;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++)tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    bool check(int x) {
        int cnt=0;
        for(int i=1;i<=n;i++) {
            if(height[i]<x)cnt=0,++ncnt;
            else {
                if(vis[belong[sa[i]]]!=ncnt)
                    vis[belong[sa[i]]]=ncnt,cnt++;
                if(vis[belong[sa[i-1]]]!=ncnt)
                    vis[belong[sa[i-1]]]=ncnt,cnt++;
                if(cnt==T)return true;
            }
        }
        return false;
    }
    
    int main() {
        cin>>T;
        for(int i=1;i<=T;i++) {
            scanf("%s",s1+1);
            for(int j=1;j<=strlen(s1+1);j++) {
                s[++n]=s1[j]-'a'+1;
                belong[n]=i;
            }
            s[++n]=26+i;
        }
        suffixsort();getheight();
        int L=1,R=n,ans=0;
        while(L<=R) {
            int mid=L+R>>1;
            if(check(mid))L=mid+1,ans=mid;
            else R=mid-1;
        }
        cout<<ans;
    }
    View Code

    乐曲主题Musical Themes P2743

    题意:

    给定一个串,求其两个相同的最长子串满足

    1、两个子串没有公共部分

    2、子串的长度最少为5 否则输出0

    3、如果一个子串的所有元素都加上一个定值等于另一个子串 那么也满足条件

    题解:

    • 这题和第一题有点类似  但是第一题已经固定好两个位置了,比较特殊,所以很简单
    • 先不考虑条件3 
    • 可以二分最大长度  和上面的题目类似  维护sa的最大值和最小值  然后通过最大值和最小值的差值判断是否满足条件
    • 再来看条件三 可以对串进行相邻两项做差即可
    • 注意$maxx-minn>x $ 并没有等于  因为相邻的插项映射回原序列会有一个公共部分
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=3e5+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N];
    int s[N],b[N];
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[sum[rk[tp[i]]]--]=tp[i];
    }
    
    void suffixsort() {
        for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++) tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    bool check(int x) {
        int maxx,minn;
        for(int i=1;i<=n;i++) {
             if(height[i]<x) maxx=minn=sa[i];
             else {
                maxx=max(maxx,sa[i]);
                minn=min(minn,sa[i]);
                if(maxx-minn>x)return true;
             }
        }
        return false;
    }
    
    int main() {
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d",&s[i]);
        for(int i=1;i<=n-1;i++) s[i]=s[i+1]-s[i],b[i]=s[i];
        n--;
        sort(b+1,b+1+n);
        M=unique(b+1,b+1+n)-b-1;
        for(int i=1;i<=n;i++) s[i]=lower_bound(b+1,b+1+M,s[i])-b;
        suffixsort(); getheight();
        int L=1,R=n,ans=0;
        while(L<=R) {
            int mid=L+R>>1;
            if(check(mid))ans=mid,L=mid+1;else R=mid-1;
        }
        printf("%d
    ",ans>=4?ans+1:0);
    }
    View Code

    DISUBSTR - Distinct Substrings  SP694 

    题意:

    给定一个串,问有多少个内容不同的子串

    题解:

    答案为总的减去重复的

    很显然总的为 n*(n+1)/2 重复的为 sum( height[i] )

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=2e3+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N];
    char s[N];
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[sum[rk[tp[i]]]--]=tp[i];
    }
    
    void suffixsort() {
        for(int i=1;i<=n;i++) rk[i]=s[i]-'a'+1,tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++) tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    int main() {
        int cas; cin>>cas;
        while(cas--) {
            scanf("%s",s+1);
            n=strlen(s+1);
            for(int i=1;i<=n;i++) if(isupper(s[i]))s[i]+=32;
            M=300;
            int ans=n*(n+1)/2;
            suffixsort(); getheight();
            for(int i=2;i<=n;i++)ans-=height[i];
            cout<<ans<<endl;
        }
    }
    View Code

     

    Sandy的卡片 P2463

    题意:

    见链接

    题解:

    • 就是最长公共串那题 和 乐曲主题那题的结合版
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=2e5+10;
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N],T;
    int s[N],belong[N],b[N],a[N],vis[N],ncnt;
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[sum[rk[tp[i]]]--]=tp[i];
    }
    
    void suffixsort() {
        for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++) tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    bool check(int x) {
        int cnt=0;++ncnt;
        for(int i=1;i<=n;i++) {
            if(height[i]<x)cnt=0,++ncnt;
            else {
                if(vis[belong[sa[i]]]!=ncnt)
                    vis[belong[sa[i]]]=ncnt,cnt++;
                if(vis[belong[sa[i-1]]]!=ncnt)
                    vis[belong[sa[i-1]]]=ncnt,cnt++;
                if(cnt>=T)return true;
            }
        }
        return false;
    }
    
    int main() {
        cin>>T;
        for(int i=1;i<=T;i++) {
            int x;scanf("%d",&x);
            for(int j=1;j<=x;j++) scanf("%d",&a[j]);
            x--;
            for(int j=1;j<=x;j++) a[j]=a[j+1]-a[j],s[++n]=a[j],b[n]=a[j],belong[n]=i;
            s[++n]=10000+i;b[n]=s[n];
        }
        sort(b+1,b+1+n);
        M=unique(b+1,b+1+n)-b-1;
        for(int i=1;i<=n;i++) s[i]=lower_bound(b+1,b+1+M,s[i])-b;
        suffixsort(); getheight();
    
        int L=1,R=n,ans=0;
        while(L<=R) {
            int mid=L+R>>1;
            if(check(mid))ans=mid,L=mid+1;else R=mid-1;
        }
        cout<<ans+1;
        return 0;
    }
    View Code

    K-th occurrence hdu6704

    题意:

    给定一个串S, 有m个询问: 求子串S[l,r]在S第k次出现的位置

    题解:

    • 求相同子串可以从height数组入手
    • 首先找到该串的后缀排名pos 然后求出往右延展,也就是往排名靠后的后缀的的最右端R,使得pos-R的height均满足>=子串长度  这就满足了这些后缀的前缀一定和s[l,r]相同 向左同理
    • 这个过程如何加速呢?可以考虑st表+二分 ,注意边界条件  因为height[i]是表示排名为i的串和排名为i-1的串的lcp
    • 所以我们得到了一个名次范围 L-R 这些名次的后缀的前缀均满足条件
    • 所以问题转化为我们要求 sa[L-R]里的第k大  用主席树维护一下就好了
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+10;
    int t[N<<5],lson[N<<5],rson[N<<5],T[N],ncnt,m;
    
    void update(int x,int l,int r,int pre,int &pos) {
        pos=++ncnt;
        t[pos]=t[pre]+1;lson[pos]=lson[pre];rson[pos]=rson[pre];
        if(l==r) return ;
        int m=l+r>>1;
        if(x<=m)update(x,l,m,lson[pre],lson[pos]);
        else update(x,m+1,r,rson[pre],rson[pos]);
    }
    
    int qk(int k,int l,int r,int pre,int pos) {
        if(l==r)return l;int m=l+r>>1;
        int x=t[lson[pos]]-t[lson[pre]];
        if(k<=x)return qk(k,l,m,lson[pre],lson[pos]);
        else return qk(k-x,m+1,r,rson[pre],rson[pos]);
    }
    
    int n,M,sa[N],rk[N],tp[N],sum[N],height[N],st_min[N][20],lg[N];
    char s[N];
    
    void Qsort() {
        for(int i=0;i<=M;i++) sum[i]=0;
        for(int i=1;i<=n;i++) sum[rk[i]]++;
        for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
        for(int i=n;i>=1;i--) sa[sum[rk[tp[i]]]--]=tp[i];
    }
    
    void suffixsort() {
        for(int i=1;i<=n;i++) rk[i]=s[i],tp[i]=i;
        Qsort();
        for(int w=1,p=0;p<n;M=p,w<<=1) {
            p=0;
            for(int i=1;i<=w;i++) tp[++p]=n-w+i;
            for(int i=1;i<=n;i++) if(sa[i]>w)tp[++p]=sa[i]-w;
            Qsort();
            swap(tp,rk);
            rk[sa[1]]=p=1;
            for(int i=2;i<=n;i++)
                rk[sa[i]]=( tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w] )?p:++p;
        }
    }
    
    void getheight() {
        int j,k=0;
        for(int i=1;i<=n;i++) {
            if(k)k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    int getmin(int l,int r) {
        int len=lg[r-l+1];
        return min(st_min[l][len],st_min[r-(1<<len)+1][len]);
    }
    
    int solve(int l,int r,int k) {
        int len=r-l+1,hpos=rk[l];
        int L=hpos+1,R=n,ansR=hpos;
        while(L<=R) {
            int mid=L+R>>1;
            if(getmin(hpos+1,mid)>=len)ansR=mid,L=mid+1;
            else R=mid-1;
        }
        L=1,R=hpos;int ansL=hpos;
        while(L<=R) {
            int mid=L+R>>1;
            if(getmin(mid,hpos)>=len)R=mid-1,ansL=mid-1;
            else L=mid+1;
        }
        if(ansR-ansL+1<k)return -1;
        return qk(k,1,1e5,T[ansL-1],T[ansR]);
    }
    
    int main() {
        lg[0]=-1;
        for(int i=1;i<=100000;i++)lg[i]=lg[i/2]+1;
        int cas;cin>>cas;
        while(cas--) {
            ncnt=0;
            cin>>n>>m;
            scanf("%s",s+1);
            for(int i=1;i<=n;i++)s[i]=s[i]-'a'+1;
            M=30;
            suffixsort(); getheight();
            for(int i=1;i<=n;i++) update(sa[i],1,1e5,T[i-1],T[i]);
            for(int i=1;i<=n;i++) st_min[i][0]=height[i];
            for(int i=1;i<=lg[n];i++) {
                for(int j=1;j+(1<<i)-1<=n;j++)
                    st_min[j][i]=min(st_min[j][i-1],st_min[j+(1<<(i-1))][i-1]);
            }
            int l,r,k;
            while(m--) {
                scanf("%d%d%d",&l,&r,&k);
                printf("%d
    ",solve(l,r,k));
            }
        }
    }
    View Code
  • 相关阅读:
    Opencv CamShift+Kalman目标跟踪
    Opencv混合高斯模型前景分离
    TTabControl、TMemo组件(制作一个简单的多文本编辑框)
    ShowMessage和MessageDlg消息对话框(VCL)
    TPageControl组件
    TImageList 和 TlistView 组件(C++Builder)
    C# 动态链接库的创建
    线程的并发与并行
    OpenCv haar+SVM训练的xml检测人头位置
    C++Builder组件
  • 原文地址:https://www.cnblogs.com/bxd123/p/11793833.html
Copyright © 2011-2022 走看看