zoukankan      html  css  js  c++  java
  • POJ 1743:后缀数组

    求满足下列要求的最长子串:

    1.长度不小于5

    2.出现两次(子串整体加上某个值也算出现一次

    3.两次出现无重叠

    难处理的是第二个要求:整体增加某个数也算出现一次

    稍微思考一下便会得出:整体加上某个值后,相邻数的差值是不变的

    所以用一个数组r[i]保存mus[i+1]与mus[i]的差值

    这里要注意将差值加上88,使差值始终为正以免在计算sa、rank的时候RE,同时字符集大小也变成了88*2

    二分答案的同时将后缀按大于mid height分组,满足要求的子串肯定是同一组内的后缀的公共前缀

    用l、r记录子串范围,r-l则为两个后缀起始位置距离,若r-l>mid则符合要求(不重叠)

    这里要注意的是:

    1.sa对应的是差值的后缀,所以反馈到初始数组的话,ans需要+1(5个数字只有4个差值)

    2.计算height的时候传入n-1,以免出现n=1的情况使得rank[0]-1=-1而出现RE

    #include"cstdio"
    #include"queue"
    #include"cmath"
    #include"stack"
    #include"iostream"
    #include"algorithm"
    #include"cstring"
    #include"queue"
    #include"map"
    #include"set"
    #include"vector"
    #define ll long long
    #define mems(a,b) memset(a,b,sizeof(a))
    #define ls pos<<1
    #define rs pos<<1|1
    
    using namespace std;
    const int MAXE = 500050;
    const int MAXN = 20005;
    const int INF = 0x3f3f3f3f;
    
    int sa[MAXN];
    int X[MAXN],Y[MAXN],radix[MAXN],trank[MAXN];
    int rank[MAXN];
    int mus[MAXN],r[MAXN];
    int n;
    
    void get_sa(int *r,int *sa,int n,int m){
        int i,j,p,*Rank=X,*tsa=Y,*t;
        for (i=0;i<m;i++)  radix[i]=0;
        for (i=0;i<n;i++) radix[Rank[i]=r[i]]++;
        for (i=1;i<m;i++)  radix[i]+=radix[i-1];
        for (i=n-1;i>=0;i--)  sa[--radix[Rank[i]]]=i;
        for (j=1,p=1;p<n;j*=2,m=p)
        {
            for (p=0,i=n-j;i<n;i++) tsa[p++]=i;
            for (i=0;i<n;i++) if (sa[i]>=j) tsa[p++]=sa[i]-j;
            for (i=0;i<n;i++) trank[i]=Rank[tsa[i]];
            for (i=0;i<m;i++) radix[i]=0;
            for (i=0;i<n;i++) radix[trank[i]]++;
            for (i=1;i<m;i++) radix[i]+=radix[i-1];
            for (i=n-1;i>=0;i--) sa[--radix[trank[i]]]=tsa[i];
            for (t=Rank,Rank=tsa,tsa=t,p=1,Rank[sa[0]]=0,i=1;i<n;i++){
                if(tsa[sa[i-1]]==tsa[sa[i]]&&tsa[sa[i-1]+j]==tsa[sa[i]+j]) Rank[sa[i]]=p-1;
                else Rank[sa[i]]=p++;
            }
        }
        for(i = 0; i < n; i ++) rank[sa[i]] = i;
        //for(i=0;i<n;i++) cout<<height[i]<<' ';cout<<endl;
    }
    int height[MAXN];
    void get_height(int *r,int *sa,int n){
        int i,j=0,k=0;
        for(i = 0; i < n; height[rank[i ++]] = k)
        for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
    }
    
    bool check(int mid){
        int l=sa[0],r=sa[0];
        for(int i=1;i<n;i++){
            if(height[i]>=mid){
                l=min(l,sa[i]);
                r=max(r,sa[i]);
                if(r-l>mid) return true;
            }
            else l=r=sa[i];
        }
        return false;
    }
    
    int main(){
        //freopen("in.txt","r",stdin);
        while(scanf("%d",&n)&&n){
            for(int i=0;i<n;i++) scanf("%d",&mus[i]);
            for(int i=0;i<n-1;i++) r[i]=mus[i+1]-mus[i]+88;
            r[n-1]=0;
            get_sa(r,sa,n,90*2);
            get_height(r,sa,n-1);
            int ans=0;
            int low=0,high=n,mid;
            while(low<=high){
                mid=(low+high)>>1;
                if(check(mid)){
                    ans=mid;
                    low=mid+1;
                }
                else high=mid-1;
            }
            if(ans<4) ans=0;
            else ans++;
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    称重量
    计算机网络
    进程间的通信方式与区别
    求两IP是否在同一局域网(运子网掩码用)
    Linux Redhat7 磁盘阵列基本原理
    Linux Redhat7更改root用户密码
    更改Linuxshell类型
    linux系统下的/var/spool/mail/root
    centos 解决"不在 sudoers 文件中。此事将被报告
    redhat 7 安装图形界面
  • 原文地址:https://www.cnblogs.com/luxiaoming/p/5236941.html
Copyright © 2011-2022 走看看