后缀数组
小白之作,自此保留模板,感想日后在作修改
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)左右。

浙公网安备 33010602011771号