zoukankan      html  css  js  c++  java
  • 后缀数组的应用

    后缀排序

    题目链接: P3809【模板】后缀排序
    sa[i]表示排名为i的后缀的起始位置的下标

    #include<bits/stdc++.h>
    using namespace std;
    const int maxx = 1e6+10;
    char s[maxx];
    int y[maxx],x[maxx],c[maxx],sa[maxx];
    int rk[maxx],height[maxx],wt[30];
    int n,m;
    void get_sa()
    {
        for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
        //c数组是桶
        //x[i]是第i个元素的第一关键字
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;i++)y[++num]=i;
            //y[i]表示第二关键字排名为i的数,第一关键字的位置
            //第n-k+1到第n位是没有第二关键字的,所以排名在最前面
            for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++)c[i]=0;
            for(int i=1;i<=n;i++)c[x[i]]++;
            for(int i=2;i<=m;i++)c[i]+=c[i-1];
            for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
            //因为y的顺序是按照第二关键字的顺序来排的
            //第二关键字靠后的,在同一个关键字桶中排名越靠后
            //基数排序
            swap(x,y);
            x[sa[1]]=1;num=1;
            for(int i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
            if(num==n)break;
            m=num;
        }
        for(int i=1;i<=n;i++)
            printf("%d ",sa[i]);
        printf("
    ");
    }
    void getheight()
    {
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    int main()
    {
        scanf("%s",s+1);
        n=strlen(s+1);m=122;
        get_sa();
        return 0;
    }
    
    

    求最大公共前缀

    rk[i]就表示起始位置的下标为i的后缀的排名
    height[i]表示排名为i-1的后缀和排名为i的后缀的最大公共前缀
    height[i]有一个很重要的性质
    定义LCP(i,j)为排名为i的后缀和排名为j的后缀的最大公共前缀
    LCP(i,k)=min(LCP[i,i+1],LCP[i+1,i+2]...LCP[k-1,k])
    LCP[i,k]=min(height[i+1],height[i+2]...height[k])

    void get_height()
    {
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    

    后缀数组的应用

    求两个字符串的最长公共子串

    题目链接: poj2774 Long Long Message
    思路:将两个字符串连起来,中间用特殊符号隔开,然后跑后缀数组,求满足sa[i-1]和sa[i]在特殊符号两边的height[i]的最大值

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int maxx = 1e6+10;
    char s[maxx];
    int y[maxx],x[maxx],c[maxx],sa[maxx];
    int rk[maxx],height[maxx];
    int n,m;
    void get_sa()
    {
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;i++)y[++num]=i;
            for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++)c[i]=0;
            for(int i=1;i<=n;i++)c[x[i]]++;
            for(int i=2;i<=m;i++)c[i]+=c[i-1];
            for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;num=1;
            for(int i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
            if(num==n)break;
            m=num;
        }
    }
    void get_height()
    {
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    int main()
    {
        scanf("%s",s+1);
        int len1=strlen(s+1);
        s[++len1]='0';
        scanf("%s",s+len1+1);
        int len2=strlen(s+1);
        m=122;n=len2;
        get_sa();
        get_height();
        int ans=0;
        for(int i=2;i<=len2;i++)
        {
            if(height[i]>ans)
            {
                if((len1<sa[i]&&len1>sa[i-1])||(len1>sa[i]&&len1<sa[i-1]))
                    ans=height[i];
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    求多个字符串的最长公共子串

    题目链接: poj3450 Corporate Identity
    思路:将每个字符串用特殊字符分隔连成一串,然后二分枚举长度,通过height[i]判断是否有n个字符串的最长公共子串为这个长度

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxx = 800010;
    char ch[210];
    int y[maxx],x[maxx],c[maxx],sa[maxx];
    int rk[maxx],height[maxx];
    int s[maxx];
    int pos[maxx],vis[4010];
    int n,m,t,anspos;
    void get_sa()
    {
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;i++)y[++num]=i;
            for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++)c[i]=0;
            for(int i=1;i<=n;i++)c[x[i]]++;
            for(int i=2;i<=m;i++)c[i]+=c[i-1];
            for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;num=1;
            for(int i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
            if(num==n)break;
            m=num;
        }
    }
    void get_height()
    {
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    int judge(int mid)
    {
        int i=2;
        for(int i=2;i<=n;i++)
        {
            if(height[i]<mid)continue;
            int cnt=0;
            for(int j=1;j<=t;j++)vis[j]=0;
            while(height[i]>=mid&&i<=n)
            {
                if(!vis[pos[sa[i-1]]])
                {
                    vis[pos[sa[i-1]]]=1;
                    cnt++;
                }
                i++;
            }
            if(!vis[pos[sa[i-1]]])
            {
                vis[pos[sa[i-1]]]=1;
                cnt++;
            }
            if(cnt>=t)
            {
                anspos=sa[i-1];
                return 1;
            }
        }
        return 0;
    }
    int main()
    {
        while(~scanf("%d",&t)&&t)
        {
            m=30;
            n=0;
            int len,ma=210;
            for(int i=1;i<=t;i++)
            {
                scanf("%s",ch);
                len=strlen(ch);
                ma=min(ma,len);
                for(int j=0;j<len;j++)
                {
                    s[++n]=ch[j]-'a'+1;
                    pos[n]=i;
                }
                s[++n]=++m;
            }
            get_sa();
            get_height();
            int l=1,r=ma;
            int ans=0;
            while(l<=r)
            {
                int mid=(l+r)/2;
                if(judge(mid))ans=mid,l=mid+1;
                else r=mid-1;
            }
            if(ans==0)printf("IDENTITY LOST
    ");
            else
            {
                for(int i=anspos;i<anspos+ans;i++)
                    printf("%c",s[i]+'a'-1);
                printf("
    ");
            }
        }
        return 0;
    }
    

    求多个字符串中不少于k个字符串的最长公共子串

    题目链接:poj3294 Life Forms
    思路:跟上面一样,判断的时候只要判断是否有超过n/2个字符串的的最长公共子串为该长度,这道题还要特判一下n=1

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxx = 1000010;
    char ch[1010];
    int y[maxx],x[maxx],c[maxx],sa[maxx];
    int rk[maxx],height[maxx];
    int s[maxx];
    int pos[maxx],vis[110];
    int n,m,t,anspos[maxx];
    void get_sa()
    {
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;i++)y[++num]=i;
            for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++)c[i]=0;
            for(int i=1;i<=n;i++)c[x[i]]++;
            for(int i=2;i<=m;i++)c[i]+=c[i-1];
            for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y);
            x[sa[1]]=1;num=1;
            for(int i=2;i<=n;i++)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
            if(num==n)break;
            m=num;
        }
    }
    void get_height()
    {
        int k=0;
        for(int i=1;i<=n;i++)rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1)continue;
            if(k)k--;
            int j=sa[rk[i]-1];
            while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    int judge(int mid)
    {
        int i=2,tot=0;
        for(int i=2;i<=n;i++)
        {
            if(height[i]<mid)continue;
            int cnt=0;
            for(int j=1;j<=t;j++)vis[j]=0;
            while(height[i]>=mid&&i<=n)
            {
                if(!vis[pos[sa[i-1]]])
                {
                    vis[pos[sa[i-1]]]=1;
                    cnt++;
                }
                i++;
            }
            if(!vis[pos[sa[i-1]]])
            {
                vis[pos[sa[i-1]]]=1;
                cnt++;
            }
            if(cnt>t/2)anspos[++tot]=sa[i-1];
        }
        if(tot)
        {
            anspos[0]=tot;
            return 1;
        }
        else return 0;
    }
    int main()
    {
        int cas=0;
        while(~scanf("%d",&t)&&t)
        {
            if(cas++)printf("
    ");
            if(t==1)
            {
                scanf("%s",ch);
                printf("%s
    ",ch);
                continue;
            }
            m=30;
            n=0;
            int len,ma=0;
            for(int i=1;i<=t;i++)
            {
                scanf("%s",ch);
                len=strlen(ch);
                ma=max(ma,len);
                for(int j=0;j<len;j++)
                {
                    s[++n]=ch[j]-'a'+1;
                    pos[n]=i;
                }
                s[++n]=++m;
            }
            get_sa();
            get_height();
            int l=1,r=ma;
            int ans=0;
            while(l<=r)
            {
                int mid=(l+r)/2;
                if(judge(mid))ans=mid,l=mid+1;
                else r=mid-1;
            }
            if(ans==0)printf("?
    ");
            else
            {
                for(int i=1;i<=anspos[0];i++)
                {
                    for(int j=anspos[i];j<anspos[i]+ans;j++)
                        printf("%c",s[j]+'a'-1);
                    printf("
    ");
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    scrapy中selenium的应用
    Django的锁和事务
    redis
    【leetcode】187. Repeated DNA Sequences
    【leetcode】688. Knight Probability in Chessboard
    【leetcode】576. Out of Boundary Paths
    【leetcode】947. Most Stones Removed with Same Row or Column
    【leetcode】948. Bag of Tokens
    【leetcode】946. Validate Stack Sequences
    【leetcode】945. Minimum Increment to Make Array Unique
  • 原文地址:https://www.cnblogs.com/HooYing/p/11779976.html
Copyright © 2011-2022 走看看