后缀数组

        小白之作,自此保留模板,感想日后在作修改

  1 /*
  2  * POJ 1743 Musical Theme
  3  * 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。
  4  * “主题”是整个音符序列的一个子串,它需要满足如下条件:
  5  * 1.长度至少为5个音符
  6  * 2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。)
  7  * 3.重复出现的同一主题不能有公共部分。
  8  *
  9  * 先转化成相邻两项的差值,然后就是找不可重叠重复子串。
 10  * 做法就是二分答案LEN
 11  * 然后根据height值进行分组
 12  */
 13 
 14 #include <iostream>
 15 #include <stdio.h>
 16 #include <algorithm>
 17 #include <string.h>
 18 using namespace std;
 19 const int MAXN=20010;
 20 
 21 /*
 22 *suffix array
 23 *倍增算法  O(n*logn)
 24 *待排序数组长度为n,放在0~n-1中,在最后面补一个0
 25 *build_sa( ,n+1, );//注意是n+1;
 26 *getHeight(,n);
 27 *例如:
 28 *n   = 8;
 29 *num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
 30 *rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
 31 *sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
 32 *height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
 33 *
 34 */
 35 
 36 int sa[MAXN];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
 37              //的后缀的开头位置顺次放入SA中
 38 int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
 39 int rank[MAXN],height[MAXN];
 40 //待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
 41 //除s[n-1]外的所有s[i]都大于0,r[n-1]=0
 42 //函数结束以后结果放在sa数组中
 43 void build_sa(int s[],int n,int m)
 44 {
 45     int i,j,p,*x=t1,*y=t2;
 46     //第一轮基数排序,如果s的最大值很大,可改为快速排序
 47     for(i=0;i<m;i++)c[i]=0;
 48     for(i=0;i<n;i++)c[x[i]=s[i]]++;
 49     for(i=1;i<m;i++)c[i]+=c[i-1];
 50     for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 51     for(j=1;j<=n;j<<=1)
 52     {
 53         p=0;
 54         //直接利用sa数组排序第二关键字
 55         for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
 56         for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
 57         //这样数组y保存的就是按照第二关键字排序的结果
 58         //基数排序第一关键字
 59         for(i=0;i<m;i++)c[i]=0;
 60         for(i=0;i<n;i++)c[x[y[i]]]++;
 61         for(i=1;i<m;i++)c[i]+=c[i-1];
 62         for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
 63         //根据sa和x数组计算新的x数组
 64         swap(x,y);
 65         p=1;x[sa[0]]=0;
 66         for(i=1;i<n;i++)
 67             x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
 68         if(p>=n)break;
 69         m=p;//下次基数排序的最大值
 70     }
 71 }
 72 void getHeight(int s[],int n)
 73 {
 74     int i,j,k=0;
 75     for(i=0;i<=n;i++)rank[sa[i]]=i;
 76     for(i=0;i<n;i++)
 77     {
 78         if(k)k--;
 79         j=sa[rank[i]-1];
 80         while(s[i+k]==s[j+k])k++;
 81         height[rank[i]]=k;
 82     }
 83 }
 84 int s[MAXN];
 85 bool check(int n,int k)
 86 {
 87     int Max=sa[1],Min=sa[1];
 88     for(int i=2;i<=n;i++)
 89     {
 90         if(height[i]<k)Max=Min=sa[i];
 91         else
 92         {
 93             if(sa[i]<Min)Min=sa[i];
 94             if(sa[i]>Max)Max=sa[i];
 95             if(Max-Min>k)return true;
 96         }
 97     }
 98     return false;
 99 }
100 int main()
101 {
102     //freopen("in.txt","r",stdin);
103     //freopen("out.txt","w",stdout);
104     int n;
105     while(scanf("%d",&n)==1 && n)
106     {
107         for(int i=0;i<n;i++)scanf("%d",&s[i]);
108         for(int i=n-1;i>0;i--)s[i]=s[i]-s[i-1]+90;
109         n--;//减少一个长度
110         for(int i=0;i<n;i++)s[i]=s[i+1];
111         s[n]=0;
112         build_sa(s,n+1,200);
113         getHeight(s,n);
114         int ans=-1;
115         int l=1,r=n/2;
116         while(l<=r)
117         {
118             int mid=(l+r)/2;
119             if(check(n,mid))
120             {
121                 ans=mid;
122                 l=mid+1;
123             }
124             else r=mid-1;
125         }
126         if(ans<4)printf("0\n");
127         else printf("%d\n",ans+1);
128     }
129     return 0;
130 }

           对模板的理解么,SA数组,表示将S的n个后缀从小到大排序后把排好序的后缀的开头位置顺次放入SA中,heigh数组就是把相邻的进行对比操作,保留相邻最长公共前缀,数组的所以信息都在这里了,怎么用就看自己,复杂度因为用了倍增算法还有其他的优化,为o(n)左右。

 

posted @ 2016-07-20 19:23  热风轻浮  阅读(101)  评论(0)    收藏  举报