后缀数组 poj3693

来源:互联网 发布:武林外传排名知乎 编辑:程序博客网 时间:2024/04/30 06:43

罗大神论文里的题目……

设 $ 为一个字符串, str = abcd$$$$cdef, 则可以看出答案是 $$$$

 那用 $$$$... 与 $$$... 比较最长前缀,肯定能得到$$$

所以枚举 $ 的长度len和位置i,找以从 i 开始的后缀和以 i+len 开始的后缀的最长前缀

但如果每个位置都枚举就太慢了,所以只枚举len, 2*len, 3*len 的位置。

如果 $ 被这些位置切断了呢?那我们一定能得到一个不能除尽 len 的最长前缀长度k

那我们就检测一下前面的一个可能整的位置 i - (len - k%len) 

但这个题目还要按字典序输出……会出现这样的情况 xababa , 对于这个字符串的解应该是abab

但按上述所说,在 len=2 ,i=2 时的最长前缀 k 是可以除尽len的,不会向前检测 len=2 和 i=1 的情况。

所以本菜很无奈……就直接不管什么都向前检测到 k/len 不如当前解为止……


自己写的模板感觉有点繁琐而且不是很明确,就跟随大神的脚步了……

解题的那部分代码写的不是很好……可以再简化和优化下,AC后也懒的管了……

/*** 后缀数组模板,直接用,很难理解** poj 3693 (附带rmq模板)*/#include <stdio.h>#include <math.h>#include <string.h>const int maxn = 100001;struct SuffixArr{int wa[maxn], wb[maxn], wv[maxn], ws[maxn], rank[maxn];int cmp(int *r, int a, int b, int l){return r[a]==r[b] && r[a+l]==r[b+l];}void DA(int *r, int *sa, int *h, int n, int m){//排序后编号从1开始int i, j, p, k=0, *x=wa, *y=wb, *t;r[n++] = 0;//在尾部添加一个0字符// 先对sa排序for(i=0; i<m; i++) ws[i]=0;for(i=0; i<n; i++) ws[x[i]=r[i]]++;for(i=0; i<m; i++) ws[i]+=ws[i-1];for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;// 倍增法求safor(j=1,p=1; p<n; j*=2,m=p){for(p=0,i=n-j; i<n; i++) y[p++]=i;for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;for(i=0; i<m; i++) ws[i]=0;for(i=0; i<n; i++) ws[wv[i]=x[y[i]]]++;for(i=1; i<m; i++) ws[i]+=ws[i-1];for(i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1:p++;}// 求height数组if (!h) return ; else n--;for(i=1; i<=n; i++) rank[sa[i]]=i;for(i=0; i<n; h[rank[i++]]=k)for(k?k--:0, j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);}}SA;struct RMQ{void init(int *key, int n){for(int i=1; i<=n; i++) f[i][0]=key[i];for(int j=1; j<20; j++)for(int i=1; i+(1<<j)-1<=n; i++)f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]);}int getmin(int a, int b){if (a>b) {int t=a; a=b; b=t;}a++;//题目需要int k = (int)(log(double(b-a+1))/log(2.0));return min(f[a][k], f[b-(1<<k)+1][k]);}int min(int x, int y){return x<y ? x:y;}int f[maxn][20];}rmq;// 以下为解题所需数据int sa[maxn], height[maxn];int arr[maxn], n;char ss[maxn];bool Input(){scanf("%s", ss);if (ss[0] == '#') return false;n = strlen(ss);for (int i=0; i<n; i++)arr[i] = ss[i];return true;}void Solve(int testn){SA.DA(arr, sa, height, n, 128);rmq.init(height, n);// solveint maxloop=0, beg, span;for (int len=1; len<n/2; len++){for (int i=0; i+len<=n; i+=len){int k = rmq.getmin(SA.rank[i], SA.rank[i+len]);int lp= k/len + 1;if (lp > maxloop){maxloop = lp;span = lp*len;beg = i;}else if (lp == maxloop){if (SA.rank[i] < SA.rank[beg]){span = lp*len;beg = i;}}for (int t=i-(len-(k%len)); t>i-len; t--){if (t < 0) continue;k = rmq.getmin(SA.rank[t], SA.rank[t+len]);if (k/len+1 < lp) break;lp = k/len + 1;if (lp > maxloop){maxloop = lp;span = lp*len;beg = t;}else if (lp == maxloop){if (SA.rank[t] < SA.rank[beg]){span = lp*len;beg = t;}}}}}// outputprintf("Case %d: ", testn);//printf("loop %d beg %d ", maxloop, beg);for (int i=beg; i<beg+span; i++)printf("%c", (char)arr[i]);printf("\n");}int main(){int t=1;while (Input()){Solve(t++);}return 0;}