• 后缀数组的应用


    后缀排序

    题目链接: 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;
    }
    
  • 相关阅读:
    【iOS
    【iOS】Swift ESTabBarController的使用
    【iOS
    【iOS】图表实现-Charts(二)
    【iOS】图表实现-Charts(一)
    【iOS】图表实现-AAChartKit
    【iOS】图表实现-总述
    【iOS
    【iOS
    整理下开发中常用的第三方库
  • 原文地址:https://www.cnblogs.com/HooYing/p/11779976.html
Copyright © 2020-2023  润新知