2018寒假訓練記錄 2.7
阿新 • • 發佈:2018-02-08
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];View Code14 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 }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