2018寒假训练记录 2.7

Posted wfgu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018寒假训练记录 2.7相关的知识,希望对你有一定的参考价值。

又看了一波后缀数组的论文,放弃要完全搞明白排序的那部分的想法了,黑盒就黑盒好了。

注意的地方是数组最后要加一个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的主要内容,如果未能解决你的问题,请参考以下文章

2018年全国多校算法寒假训练营练习比赛(第一场)G 圆圈

2.7寒假学习记录

2018年全国多校算法寒假训练营练习比赛(第三场)

2018年全国多校算法寒假训练营练习比赛(第五场)题解

2018年全国多校算法寒假训练营练习比赛(第一场)C 六子冲

2018年全国多校算法寒假训练营练习比赛(第三场)题解