又看了一波后缀数组的论文,放弃要完全搞明白排序的那部分的想法了,黑盒就黑盒好了。
注意的地方是数组最后要加一个0,理解sa, height, rank这几个数组的意义与用处。
sa[i]:排名i的后缀的起始位置
height[i]:suffix(sa[i - 1)和suffix(sa[i])的lcp
rank[i]:起始位置i的后缀的排名
然后就是跟着论文爆炸写题。
POJ 1743
差分之后求一个不可重叠最长重复子串。
二分答案check。
后缀分组:利用height值把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。
对于每组后缀,判断是否存在两个后缀的起始位置大于k,因为这里是差分过的不能取等号。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <map> 6 #include <vector> 7 #include <set> 8 using namespace std; 9 10 const int inf = 0x3f3f3f3f; 11 const int N = 100005; 12 13 int wa[N], wb[N], wv[N], wc[N]; 14 int sa[N], rk[N], height[N]; 15 int cmp(int *r,int a,int b,int l){ 16 return r[a]==r[b] && r[a+l]==r[b+l]; 17 } 18 void build(int *r, int n, int m) { 19 int *x=wa,*y=wb; 20 21 for(int i=0; i<m; ++i) wc[i]=0; 22 for(int i=0; i<n; ++i) wc[x[i]=r[i]]++; 23 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 24 for(int i=n-1; i>=0; --i) sa[--wc[x[i]]]=i; 25 26 int p=1; 27 for(int j=1; p<n; j<<=1,m=p){ 28 p=0; 29 for(int i=n-j; i<n; ++i) y[p++]=i; 30 for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j; 31 for(int i=0; i<n; ++i) wv[i]=x[y[i]]; 32 for(int i=0; i<m; ++i) wc[i]=0; 33 for(int i=0; i<n; ++i) wc[wv[i]]++; 34 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 35 for(int i=n-1; i>=0; --i) sa[--wc[wv[i]]]=y[i]; 36 swap(x,y); x[sa[0]]=0; p=1; 37 for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 38 } 39 40 for(int i=1; i<n; ++i) rk[sa[i]]=i; 41 int k=0; 42 for(int i=0; i<n-1; height[rk[i++]]=k){ 43 if(k) --k; 44 for(int j=sa[rk[i]-1]; r[i+k]==r[j+k]; ++k); 45 } 46 } 47 48 int n, s[N]; 49 bool check(int k) { 50 int mn = inf; 51 int mx = -inf; 52 for (int i = 1; i <= n; ++i) { 53 if (height[i] >= k) { 54 mn = min(mn, min(sa[i], sa[i - 1])); 55 mx = max(mx, max(sa[i], sa[i - 1])); 56 if (mx - mn > k) return 1; 57 } else { 58 mn = sa[i]; 59 mx = sa[i]; 60 } 61 } 62 return 0; 63 } 64 65 int main() { 66 while (scanf("%d", &n) != EOF && n) { 67 for (int i = 0; i < n; ++i) { 68 scanf("%d", s + i); 69 } 70 for (int i = 0; i + 1 < n; ++i) { 71 s[i] = s[i + 1] - s[i]; 72 } 73 for (int i = 0; i < n - 1; ++i) { 74 s[i] += 100; 75 } 76 s[n - 1] = 0; 77 78 // printf("before build\n"); 79 build(s, n, 256); 80 81 // printf("after build\n"); 82 int lb = 0, ub = N - 1, ans = 0; 83 while (lb <= ub) { 84 int mid = (lb + ub) / 2; 85 if (check(mid)) { 86 ans = mid; 87 lb = mid + 1; 88 } else { 89 ub = mid - 1; 90 } 91 } 92 printf("%d\n", ans >= 4? ans + 1: 0); 93 } 94 return 0; 95 }
POJ 3261
求一个可重叠的k次最长重复子串。
这个k要--k。
二分答案,后缀分组,判断有没有一个组的后缀个数不小于k。
或者是可以用单调队列,维护一个大小为k的滑动窗口,取里面最小值中的最大值。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <map> 6 #include <vector> 7 #include <set> 8 using namespace std; 9 10 const int inf = 0x3f3f3f3f; 11 const int N = 100005; 12 13 int wa[N], wb[N], wc[N], wv[N]; 14 int sa[N], rk[N], height[N]; 15 int cmp(int *r,int a,int b,int l){ 16 return r[a]==r[b] && r[a+l]==r[b+l]; 17 } 18 void build(int *r, int n, int m) { 19 int *x=wa,*y=wb; 20 21 for(int i=0; i<m; ++i) wc[i]=0; 22 for(int i=0; i<n; ++i) wc[x[i]=r[i]]++; 23 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 24 for(int i=n-1; i>=0; --i) sa[--wc[x[i]]]=i; 25 26 int p=1; 27 for(int j=1; p<n; j<<=1,m=p){ 28 p=0; 29 for(int i=n-j; i<n; ++i) y[p++]=i; 30 for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j; 31 for(int i=0; i<n; ++i) wv[i]=x[y[i]]; 32 for(int i=0; i<m; ++i) wc[i]=0; 33 for(int i=0; i<n; ++i) wc[wv[i]]++; 34 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 35 for(int i=n-1; i>=0; --i) sa[--wc[wv[i]]]=y[i]; 36 swap(x,y); x[sa[0]]=0; p=1; 37 for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 38 } 39 40 for(int i=1; i<n; ++i) rk[sa[i]]=i; 41 int k=0; 42 for(int i=0; i<n-1; height[rk[i++]]=k){ 43 if(k) --k; 44 for(int j=sa[rk[i]-1]; r[i+k]==r[j+k]; ++k); 45 } 46 } 47 48 int n, k, s[N]; 49 struct Deque { 50 int v, i; 51 } Q[N]; 52 int head, tail; 53 int main() { 54 scanf("%d %d", &n, &k); k--; 55 for (int i = 0; i < n; ++i) scanf("%d", s + i); 56 vector<int> b; 57 for (int i = 0; i < n; ++i) b.push_back(s[i]); 58 sort(b.begin(), b.end()); 59 b.erase(unique(b.begin(), b.end()), b.end()); 60 for (int i = 0; i < n; ++i) s[i] = lower_bound(b.begin(), b.end(), s[i]) - b.begin() + 1; 61 n++; s[n - 1] = 0; 62 build(s, n, n); 63 64 int *x = height, ans = 0; 65 head = 1; tail = 0; 66 for (int i = 1; i < k; ++i) { 67 while (head <= tail && Q[tail].v >= x[i]) --tail; 68 ++tail; Q[tail].v = x[i]; Q[tail].i = i; 69 } 70 for (int i = k; i <= n; ++i) { 71 while (head <= tail && Q[tail].v >= x[i]) --tail; 72 ++tail; Q[tail].v = x[i]; Q[tail].i = i; 73 while (head <= tail && Q[head].i < i - k + 1) ++head; 74 ans = max(ans, Q[head].v); 75 } 76 77 printf("%d\n", ans); 78 return 0; 79 }
SPOJ - SUBST1
求不相同子串个数。
每个子串一定是某个后缀的前缀,也就是求所有后缀之间不相同的前缀的个数。
每加一个suffix(sa[i]),产生n-sa[i]+1个新的前缀,其中有height[i]个是和前面的字符串产生前缀相同。
答案就是$\Sigma n - sa[i] + 1 - height[i]$。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <map> 6 #include <vector> 7 #include <set> 8 using namespace std; 9 10 const int inf = 0x3f3f3f3f; 11 const int N = 100005; 12 13 int wa[N], wb[N], wv[N], wc[N]; 14 int sa[N], rk[N], height[N]; 15 int cmp(int *r,int a,int b,int l){ 16 return r[a]==r[b] && r[a+l]==r[b+l]; 17 } 18 void build(char *r, int n, int m) { 19 int *x=wa,*y=wb; 20 21 for(int i=0; i<m; ++i) wc[i]=0; 22 for(int i=0; i<n; ++i) wc[x[i]=r[i]]++; 23 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 24 for(int i=n-1; i>=0; --i) sa[--wc[x[i]]]=i; 25 26 int p=1; 27 for(int j=1; p<n; j<<=1,m=p){ 28 p=0; 29 for(int i=n-j; i<n; ++i) y[p++]=i; 30 for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j; 31 for(int i=0; i<n; ++i) wv[i]=x[y[i]]; 32 for(int i=0; i<m; ++i) wc[i]=0; 33 for(int i=0; i<n; ++i) wc[wv[i]]++; 34 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 35 for(int i=n-1; i>=0; --i) sa[--wc[wv[i]]]=y[i]; 36 swap(x,y); x[sa[0]]=0; p=1; 37 for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 38 } 39 40 for(int i=1; i<n; ++i) rk[sa[i]]=i; 41 int k=0; 42 for(int i=0; i<n-1; height[rk[i++]]=k){ 43 if(k) --k; 44 for(int j=sa[rk[i]-1]; r[i+k]==r[j+k]; ++k); 45 } 46 } 47 48 int n; 49 char s[N]; 50 int main() { 51 int T; 52 scanf("%d", &T); 53 while (T--) { 54 scanf("%s", s); 55 n = strlen(s); 56 n++; s[n - 1] = ‘\0‘; 57 build(s, n, 128); 58 n--; 59 long long ans = 1LL * n * (n + 1) / 2; 60 for (int i = 1; i <= n; ++i) { 61 ans -= (long long)height[i]; 62 } 63 printf("%lld\n", ans); 64 } 65 return 0; 66 }
POJ 2774
求两个串的最长公共子串
把两个串拼在一起,height[i]中的最大值就是我们要的答案,但是注意有个条件:sa[i-1]和sa[i]不能属于的同一个串才行。
这个题还有学到了一个snprintf函数,挺好用的。
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cassert> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 #include <map> 8 #include <vector> 9 #include <set> 10 using namespace std; 11 12 const int inf = 0x3f3f3f3f; 13 const int N = 100000 * 3; 14 15 int wa[N], wb[N], wv[N], wc[N]; 16 int sa[N], rk[N], height[N]; 17 int cmp(int *r,int a,int b,int l){ 18 return r[a]==r[b] && r[a+l]==r[b+l]; 19 } 20 void build(char *r, int n, int m) { 21 int *x=wa,*y=wb; 22 23 for(int i=0; i<m; ++i) wc[i]=0; 24 for(int i=0; i<n; ++i) wc[x[i]=r[i]]++; 25 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 26 for(int i=n-1; i>=0; --i) sa[--wc[x[i]]]=i; 27 28 int p=1; 29 for(int j=1; p<n; j<<=1,m=p){ 30 p=0; 31 for(int i=n-j; i<n; ++i) y[p++]=i; 32 for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j; 33 for(int i=0; i<n; ++i) wv[i]=x[y[i]]; 34 for(int i=0; i<m; ++i) wc[i]=0; 35 for(int i=0; i<n; ++i) wc[wv[i]]++; 36 for(int i=1; i<m; ++i) wc[i]+=wc[i-1]; 37 for(int i=n-1; i>=0; --i) sa[--wc[wv[i]]]=y[i]; 38 swap(x,y); x[sa[0]]=0; p=1; 39 for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 40 } 41 42 for(int i=1; i<n; ++i) rk[sa[i]]=i; 43 int k=0; 44 for(int i=0; i<n-1; height[rk[i++]]=k){ 45 if(k) --k; 46 for(int j=sa[rk[i]-1]; r[i+k]==r[j+k]; ++k); 47 } 48 } 49 50 int n; 51 char s1[N], s2[N], s[N]; 52 int main() { 53 scanf("%s%s", s1, s2); 54 int n = snprintf(s, sizeof(s), "%s%s%s", s1, "$", s2); 55 assert(n >= 0); 56 build(s, n + 1, 128); 57 58 int ans = 0, m = strlen(s1); 59 for (int i = 1; i <= n; ++i) { 60 if ((sa[i - 1] < m && sa[i] >= m) || (sa[i - 1] >= m && sa[i] < m)) { 61 ans = max(ans, height[i]); 62 } 63 } 64 printf("%d\n", ans); 65 return 0; 66 }