1. 程式人生 > >2018寒假訓練記錄 2.7

2018寒假訓練記錄 2.7

wap gif closed esp 滑動 begin push swap tro

又看了一波後綴數組的論文,放棄要完全搞明白排序的那部分的想法了,黑盒就黑盒好了。

註意的地方是數組最後要加一個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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

2018寒假訓練記錄 2.7