很久没写过这东西了,复习一波。
3238: [Ahoi2013]差异
单调栈维护height数组,由于height是递增的,所以维护单调栈中维护每个height出现的次数。(还可以两遍单调栈求一个点是最小值的区间)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 500005; 20 21 char s[N]; 22 int t1[N], t2[N], c[N], sa[N], rnk[N], height[N], m = 130, n; 23 LL sk[N], cnt[N]; 24 25 void getsa() { 26 int *x = t1, *y = t2, i, p; 27 for (i = 1; i <= m; ++i) c[i] = 0; 28 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 29 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 30 for (i = 1; i <= n; ++i) sa[c[x[i]]--] = i; 31 for (int k = 1; k <= n; k <<= 1) { 32 p = 0; 33 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 34 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 35 for (i = 1; i <= m; ++i) c[i] = 0; 36 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 37 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 38 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 39 swap(x, y); 40 p = 2; 41 x[sa[1]] = 1; 42 for (i = 2; i <= n; ++i) 43 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 44 if (p > n) break; 45 m = p; 46 } 47 } 48 void getheight() { 49 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 50 int k = 0; 51 height[1] = 0; 52 for (int i = 1; i <= n; ++i) { 53 if (rnk[i] == 1) continue; 54 if (k) k --; 55 int j = sa[rnk[i] - 1]; 56 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 57 height[rnk[i]] = k; 58 } 59 } 60 int main() { 61 scanf("%s", s + 1); 62 n = strlen(s + 1); 63 getsa(); 64 getheight(); 65 LL ans = 0; 66 for (int i = 1; i <= n; ++i) ans += 1ll * (n - 1) * i; 67 int top = 0;LL now = 0; 68 for (int i = 2; i <= n; ++i) { 69 LL tmp = 1; 70 while (top && sk[top] >= height[i]) { 71 now -= 1ll * cnt[top] * sk[top]; tmp += cnt[top]; top --; 72 } 73 sk[++top] = height[i]; cnt[top] = tmp; now += 1ll * height[i] * tmp; 74 ans -= now * 2; 75 } 76 cout << ans; 77 return 0; 78 }
CF 1090 J. Two Prefixes
题意:从第一个串中选一个前缀,从第二个串中选一个前缀,问可以组成多少个不同的串。
kmp+后缀数组。
正难则反,用总的减去出现了多次的。枚举第二个串,那么考虑那些串是已经被计算过的,考虑这个前缀的bordor,如果第一个串可以存在1~n-bordor的字符,那么就是以前计算过的。
后缀数组求lcp。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 200005; 20 char s[N], a[N], b[N]; 21 int t1[N], t2[N], c[N], sa[N], height[N], Log[N], p[N], f[N][20], rnk[N], n, m = 130, la, lb; 22 LL sum[N]; 23 24 void getsa() { 25 int *x = t1, *y = t2, i, p; 26 for (i = 1; i <= m; ++i) c[i] = 0; 27 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 28 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 29 for (i = n; i >= 1; --i) sa[c[x[i]]--] = i; 30 for (int k = 1; k <= n; k <<= 1) { 31 p = 0; 32 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 33 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 34 for (i = 1; i <= m; ++i) c[i] = 0; 35 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 36 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 37 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 38 swap(x, y); 39 p = 2; 40 x[sa[1]] = 1; 41 for (i = 2; i <= n; ++i) 42 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 43 if (p > n) break ; 44 m = p; 45 } 46 } 47 void getheight() { 48 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 49 int k = 0; 50 height[1] = 0; 51 for (int i = 1; i <= n; ++i) { 52 if (rnk[i] == 1) continue; 53 if (k) k --; 54 int j = sa[rnk[i] - 1]; 55 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 56 height[rnk[i]] = k; 57 } 58 } 59 void rmq() { 60 Log[0] = -1; 61 for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1; 62 for (int i = 1; i <= n; ++i) f[i][0] = height[i]; 63 for (int j = 1; j <= Log[n]; ++j) 64 for (int i = 1; i + (1 << j) - 1 <= n; ++i) 65 f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); 66 } 67 int LCP(int l,int r) { 68 if (l > r) swap(l, r); 69 l ++; 70 int k = Log[r - l + 1]; 71 return min(f[l][k], f[r - (1 << k) + 1][k]); 72 } 73 int main() { 74 scanf("%s%s", a + 1, b + 1); 75 la = strlen(a + 1), lb = strlen(b + 1); 76 n = la + lb; 77 for (int i = 1; i <= la; ++i) s[i] = a[i]; 78 for (int i = 1; i <= lb; ++i) s[i + la] = b[i]; 79 getsa(); 80 getheight(); 81 rmq(); 82 83 for (int i = 2; i <= la; ++i) { 84 int len = min(LCP(rnk[i], rnk[la + 1]), la - i + 1); 85 sum[len] ++; 86 } 87 for (int i = la; i >= 1; --i) sum[i] += sum[i + 1]; 88 p[1] = 0; 89 for (int i = 2; i <= lb; ++i) { 90 int j = p[i - 1]; 91 while (j && b[j + 1] != b[i]) j = p[j]; 92 if (b[j + 1] == b[i]) j ++; 93 p[i] = j; 94 } 95 LL ans = 1ll * la * lb; 96 for (int i = 2; i <= lb; ++i) 97 if (p[i]) ans -= sum[i - p[i]]; 98 cout << ans; 99 return 0; 100 }
CF 802 I. Fake News (hard)
题意:求每个串出现次数的平方。
单调栈维护height数组。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 #define mem(a) memset(a, 0, sizeof(a)) 12 using namespace std; 13 typedef long long LL; 14 15 inline int read() { 16 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 17 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 18 } 19 20 const int N = 100005; 21 char s[N]; 22 int t1[N], t2[N], c[N], sa[N], rnk[N], h[N], sk[N], pos[N], n, m = 130; 23 24 void getsa() { 25 int *x = t1, *y = t2, i, p; 26 for (i = 1; i <= m; ++i) c[i] = 0; 27 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 28 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 29 for (i = n; i >= 1; --i) sa[c[x[i]]--] = i; 30 for (int k = 1; k <= n; k <<= 1) { 31 p = 0; 32 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 33 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 34 for (i = 1; i <= m; ++i) c[i] = 0; 35 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 36 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 37 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 38 swap(x, y); 39 x[sa[1]] = 1; 40 p = 2; 41 for (i = 2; i <= n; ++i) 42 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 43 if (p > n) break; 44 m = p; 45 } 46 } 47 void getheight() { 48 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 49 int k = 0; 50 h[1] = 0; 51 for (int i = 1; i <= n; ++i) { 52 if (rnk[i] == 1) continue; 53 if (k) k --; 54 int j = sa[rnk[i] - 1]; 55 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 56 h[rnk[i]] = k; 57 } 58 } 59 void solve() { 60 scanf("%s", s + 1); 61 n = strlen(s + 1), m = 130; 62 getsa(); 63 getheight(); 64 int top = 0, now; 65 LL ans = 0, t1, t2; 66 for (int i = 2; i <= n + 1; ++i) { 67 now = i; 68 while (top && h[i] < sk[top]) { 69 t1 = i - pos[top] + 1; 70 t2 = sk[top] - max(sk[top - 1], h[i]); 71 ans += t1 * t1 * t2; 72 now = pos[top --]; 73 } 74 while (top && sk[top] == h[i]) now = pos[top --]; 75 sk[++top] = h[i], pos[top] = now; 76 } 77 for (int i = 1; i <= n; ++i) 78 ans += n - i + 1 - max(h[rnk[i]], h[rnk[i] + 1]); 79 cout << ans << "\n"; 80 } 81 int main() { 82 freopen("1.txt", "r", stdin); 83 for (int T = read(); T--; ) solve(); 84 return 0; 85 }
POJ 3581 Sequence
题意:将一个字符串分成三段,每段翻转后得到一个新字符串,是这个新字符串字典序最小。
首先将字符串反转,后缀排序,取出最小的作为第一段。剩下如果再次取后缀最小的是不可以的。因为可能取到的第一个是最小的,但是与第二个合起来后,总的字典序不是最小的。所以我们将剩下的复制一遍,然后后缀排序。这时只取前面那一部分。
样例:
6 10 1 2 2 3 4
样例来自。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 200005; 20 int s[N], t1[N], t2[N], c[N], sa[N], rnk[N], height[N], disc[N], n; 21 22 void getsa() { 23 int *x = t1, *y = t2, i, p, m = n + 10; 24 for (i = 1; i <= m; ++i) c[i] = 0; 25 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 26 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 27 for (i = n; i >= 1; --i) sa[c[x[i]]--] = i; 28 for (int k = 1; k <= n; k <<= 1) { 29 p = 0; 30 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 31 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 32 for (i = 1; i <= m; ++i) c[i] = 0; 33 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 34 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 35 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 36 swap(x, y); 37 x[sa[1]] = 1; 38 p = 2; 39 for (i = 2; i <= n; ++i) 40 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 41 if (p > n) break; 42 m = p; 43 } 44 } 45 int main() { 46 n = read(); 47 for (int i = 1; i <= n; ++i) s[i] = read(); 48 reverse(s + 1, s + n + 1); 49 50 for (int i = 1; i <= n; ++i) disc[i] = s[i]; 51 sort(disc + 1, disc + n + 1); 52 int cnt = 1; 53 for (int i = 2; i <= n; ++i) if (disc[i] != disc[cnt]) disc[++cnt] = disc[i]; 54 for (int i = 1; i <= n; ++i) s[i] = lower_bound(disc + 1, disc + cnt + 1, s[i]) - disc; 55 56 getsa(); 57 58 int k = 0; 59 for (int i = 1; k <= 2; ++i) k = sa[i]; 60 for (int i = k; i <= n; ++i) printf("%d\n",disc[s[i]]); 61 62 n = k - 1; 63 for (int i = 1; i <= k - 1; ++i) s[i + n] = s[i]; 64 n <<= 1; 65 getsa(); 66 67 for (int i = 1; k > n / 2 || k <= 1; ++i) k = sa[i]; 68 for (int i = k; i <= n / 2; ++i) printf("%d\n",disc[s[i]]); 69 for (int i = 1; i < k; ++i) printf("%d\n",disc[s[i]]); 70 71 return 0; 72 }
URAL - 1297:Palindrome
题意:求最长回文子串,如果有多个长度相同的,输出最早出现的。
分析:manacher或者后缀数组。
枚举一个点i左右分界点,然后求出$s[:i]$和$s[i:]$的lcp,后缀数组+rmq求。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 200005; 20 char s[N]; 21 int t1[N], t2[N], c[N], sa[N], height[N], Log[N], f[N][20], rnk[N], n, m = 130; 22 23 void getsa() { 24 int *x = t1, *y = t2, i, p; 25 for (i = 1; i <= m; ++i) c[i] = 0; 26 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 27 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 28 for (i = n; i >= 1; --i) sa[c[x[i]]--] = i; 29 for (int k = 1; k <= n; k <<= 1) { 30 p = 0; 31 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 32 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 33 for (i = 1; i <= m; ++i) c[i] = 0; 34 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 35 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 36 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 37 swap(x, y); 38 p = 2; 39 x[sa[1]] = 1; 40 for (i = 2; i <= n; ++i) 41 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 42 if (p > n) break ; 43 m = p; 44 } 45 } 46 void getheight() { 47 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 48 int k = 0; 49 height[1] = 0; 50 for (int i = 1; i <= n; ++i) { 51 if (rnk[i] == 1) continue; 52 if (k) k --; 53 int j = sa[rnk[i] - 1]; 54 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 55 height[rnk[i]] = k; 56 } 57 } 58 void rmq() { 59 Log[0] = -1; 60 for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1; 61 for (int i = 1; i <= n; ++i) f[i][0] = height[i]; 62 for (int j = 1; j <= Log[n]; ++j) 63 for (int i = 1; i + (1 << j) - 1 <= n; ++i) 64 f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); 65 } 66 int LCP(int l,int r) { 67 if (l > r) swap(l, r); 68 l ++; 69 int k = Log[r - l + 1]; 70 return min(f[l][k], f[r - (1 << k) + 1][k]); 71 } 72 int main() { 73 scanf("%s", s + 1); 74 n = strlen(s + 1); 75 for (int i = 1; i <= n; ++i) s[i + n] = s[n - i + 1]; 76 n <<= 1; 77 getsa(); 78 getheight(); 79 rmq(); 80 int len = 1, pos = 1; 81 for (int i = 2; i <= n; ++i) { 82 int j = n - (i - 1) + 1, now = min(min(n / 2 - i + 1, i - 1), LCP(rnk[i], rnk[j])); 83 if (now * 2 >= len) { 84 if (now * 2 > len) len = now * 2, pos = i - now; 85 else if (i - now < pos) pos = i - now; 86 } 87 j = n - (i - 1) + 1, now = min(min(n / 2 - (i + 1) + 1, i - 1), LCP(rnk[i + 1], rnk[j])); 88 if (now * 2 + 1 >= len) { 89 if (now * 2 + 1 > len) len = now * 2 + 1, pos = i - now; 90 else if (i - now + 1 < pos) pos = i - now; 91 } 92 } 93 s[pos + len] = '\0'; 94 puts(s + pos); 95 return 0; 96 }
POJ 3415:Common Substrings
题意:求两个串的求长度不小于 k 的公共子串数量。
分析:单调栈维护height数组。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 200005; 20 char s[N], a[N], b[N]; 21 int t1[N], t2[N], c[N], sa[N], rnk[N], height[N], sk[N], cnt[N], n, k; 22 23 void getsa() { 24 int *x = t1, *y = t2, i, p, m = 130; 25 for (i = 1; i <= m; ++i) c[i] = 0; 26 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 27 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 28 for (i = 1; i <= n; ++i) sa[c[x[i]]--] = i; 29 for (int k = 1; k <= n; k <<= 1) { 30 p = 0; 31 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 32 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 33 for (i = 1; i <= m; ++i) c[i] = 0; 34 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 35 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 36 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 37 swap(x, y); 38 p = 2; 39 x[sa[1]] = 1; 40 for (i = 2; i <= n; ++i) 41 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 42 if (p > n) break; 43 m = p; 44 } 45 } 46 void getheight() { 47 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 48 int k = 0; 49 height[1] = 0; 50 for (int i = 1; i <= n; ++i) { 51 if (rnk[i] == 1) continue; 52 if (k) k --; 53 int j = sa[rnk[i] - 1]; 54 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 55 height[rnk[i]] = k; 56 } 57 } 58 void solve() { 59 scanf("%s%s", a + 1, b + 1); 60 int la = strlen(a + 1), lb = strlen(b + 1); 61 n = la + lb + 1; 62 for (int i = 1; i <= la; ++i) s[i] = a[i]; 63 s[la + 1] = '\0'; 64 for (int i = 1; i <= lb; ++i) s[i + la + 1] = b[i]; 65 getsa(); 66 getheight(); 67 68 height[n + 1] = 0; 69 int top = 0, nowcnt; 70 LL ans = 0, now = 0; 71 for (int i = 2; i <= n + 1; ++i) { 72 nowcnt = 0; 73 if (height[i] < k) { top = now = 0; continue; } 74 if (sa[i - 1] <= la) nowcnt = 1, now += height[i] - k + 1; 75 while (top && sk[top] >= height[i]) { 76 now -= cnt[top] * (sk[top] - height[i]); 77 nowcnt += cnt[top]; 78 top --; 79 } 80 sk[++top] = height[i], cnt[top] = nowcnt; 81 if (sa[i] > la) ans += now; 82 } 83 84 top = nowcnt = now = 0; 85 for (int i = 2; i <= n + 1; ++i) { 86 nowcnt = 0; 87 if (height[i] < k) { top = now = 0; continue; } 88 if (sa[i - 1] > la + 1) nowcnt = 1, now += height[i] - k + 1; 89 while (top && sk[top] >= height[i]) { 90 now -= cnt[top] * (sk[top] - height[i]); 91 nowcnt += cnt[top]; 92 top --; 93 } 94 sk[++top] = height[i], cnt[top] = nowcnt; 95 if (sa[i] <= la) ans += now; 96 } 97 printf("%lld\n", ans); 98 } 99 int main() { 100 while (~scanf("%d", &k) && k) solve(); 101 return 0; 102 }
4199: [Noi2015]品酒大会
分析:后缀数组+并查集。对于第一问,r相似的对数也是r-1相似的对数,对于第二问,r相似的最大值同样也是r-1相似的最大值。
然后只对于一个height值只需要求每一段最长的区间就行了。按照height值从大的往小的依次加入,每次加入合并一个会合并至少两个串,然后求出两边的size,相乘是第一问的答案,两边的最大最小值乘起来是第二问的答案。为什么没有维护次大值次小值:考虑合并的时候,将两个区间合并成一个,如果最小值和次小值都在一边,那么这一边是height大的时候的一个区间,可以与它取max而来,否则在两边的情况就是两边的最小值。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 300005; 20 21 char s[N]; 22 int t1[N], t2[N], c[N], sa[N], rnk[N], height[N], m = 130, n; 23 int fa[N], siz[N], mx[N], mn[N], a[N]; 24 LL ans2[N], ans1[N]; 25 26 void getsa() { 27 int *x = t1, *y = t2, i, p; 28 for (i = 1; i <= m; ++i) c[i] = 0; 29 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 30 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 31 for (i = 1; i <= n; ++i) sa[c[x[i]]--] = i; 32 for (int k = 1; k <= n; k <<= 1) { 33 p = 0; 34 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 35 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 36 for (i = 1; i <= m; ++i) c[i] = 0; 37 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 38 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 39 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 40 swap(x, y); 41 p = 2; 42 x[sa[1]] = 1; 43 for (i = 2; i <= n; ++i) 44 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 45 if (p > n) break; 46 m = p; 47 } 48 } 49 void getheight() { 50 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 51 int k = 0; 52 height[1] = 0; 53 for (int i = 1; i <= n; ++i) { 54 if (rnk[i] == 1) continue; 55 if (k) k --; 56 int j = sa[rnk[i] - 1]; 57 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 58 height[rnk[i]] = k; 59 } 60 } 61 bool cmp(int x,int y) { return height[x] > height[y]; } 62 int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } 63 void Union(int x,int y) { 64 x = find(x), y = find(y); 65 if (x == y) return ; 66 if (siz[x] > siz[y]) swap(x, y); 67 fa[x] = y, siz[y] += siz[x]; 68 mx[y] = max(mx[y], mx[x]), mn[y] = min(mn[y], mn[x]); 69 } 70 int main() { 71 // freopen("1.txt", "r", stdin); 72 n = read(); 73 scanf("%s", s + 1); 74 getsa(); 75 getheight(); 76 for (int i = 1; i <= n; ++i) { 77 int x = read(); 78 mn[rnk[i]] = mx[rnk[i]] = x; 79 siz[rnk[i]] = 1; fa[rnk[i]] = rnk[i]; 80 } 81 for (int i = 1; i < n; ++i) a[i] = i + 1; 82 sort(a + 1, a + n, cmp); 83 memset(ans2, -0x3f, sizeof(ans2)); 84 for (int i = 1; i < n; ++i) { 85 int x = find(a[i] - 1), y = find(a[i]); 86 ans1[height[a[i]]] += 1ll * siz[x] * siz[y]; 87 LL tmp = max(max(1ll * mx[x] * mx[y], 1ll * mx[x] * mn[y]), max(1ll * mn[x] * mx[y], 1ll * mn[x] * mn[y])); 88 ans2[height[a[i]]] = max(ans2[height[a[i]]], tmp); 89 Union(x, y); 90 } 91 for (int i = n - 1; i >= 0; --i) { 92 ans1[i] += ans1[i + 1]; 93 ans2[i] = max(ans2[i], ans2[i + 1]); 94 } 95 for (int i = 0; i < n; ++i) 96 printf("%lld %lld\n", ans1[i], ans1[i] == 0 ? 0 : ans2[i]); 97 return 0; 98 }
4340: BJOI2015 隐身术
题意:给定两个串 A,B 。问 B 中有多少个非空子串和 A 的编辑距离不超过 K。 不同位置的内容相同的子串算作多个。两个串的“编辑距离”指的是把一个串变成另一个串需要的最小的操作次数,每次操作可以插入、删除或者替换一个字符。
分析:考虑枚举B的一个后缀i,计算这个后缀中多少个前缀是满足的。由于k比较小,考虑能否直接搜索这些位置。(x,y,z)表示当前A串在x位置,B串在y位置,前面已经修改了z次,那么可以直接求出他们的lcp,然后直接跳到这个位置。此时有三种状态(x+1,y,z+1),(x,y+1,z+1),(x+1,y+1,z+1),如果A走完了或者B走完了或者z=k了,就返回。如果A走完了或者A末尾剩下的可以全部删掉,那么就是找到合法的位置了,合法的位置可能是一段区间(因为可能会剩下一些修改的次数,可以删除一些字符),对于这个区间可以差分。
由于只会搜索k层,复杂度就是$n \times 3^k$,加上后缀数组的复杂度$nlogn$
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 200005; 20 char s[N], a[N], b[N]; 21 int t1[N], t2[N], c[N], sa[N], height[N], Log[N], f[N][20], rnk[N], n, m = 130, la, lb, k, now; 22 int sum[N]; 23 24 void getsa() { 25 int *x = t1, *y = t2, i, p; 26 for (i = 1; i <= m; ++i) c[i] = 0; 27 for (i = 1; i <= n; ++i) x[i] = s[i], c[x[i]] ++; 28 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 29 for (i = n; i >= 1; --i) sa[c[x[i]]--] = i; 30 for (int k = 1; k <= n; k <<= 1) { 31 p = 0; 32 for (i = n - k + 1; i <= n; ++i) y[++p] = i; 33 for (i = 1; i <= n; ++i) if (sa[i] > k) y[++p] = sa[i] - k; 34 for (i = 1; i <= m; ++i) c[i] = 0; 35 for (i = 1; i <= n; ++i) c[x[y[i]]] ++; 36 for (i = 1; i <= m; ++i) c[i] += c[i - 1]; 37 for (i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i]; 38 swap(x, y); 39 p = 2; 40 x[sa[1]] = 1; 41 for (i = 2; i <= n; ++i) 42 x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p - 1 : p ++; 43 if (p > n) break ; 44 m = p; 45 } 46 } 47 void getheight() { 48 for (int i = 1; i <= n; ++i) rnk[sa[i]] = i; 49 int k = 0; 50 height[1] = 0; 51 for (int i = 1; i <= n; ++i) { 52 if (rnk[i] == 1) continue; 53 if (k) k --; 54 int j = sa[rnk[i] - 1]; 55 while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k ++; 56 height[rnk[i]] = k; 57 } 58 } 59 void rmq() { 60 Log[0] = -1; 61 for (int i = 1; i <= n; ++i) Log[i] = Log[i >> 1] + 1; 62 for (int i = 1; i <= n; ++i) f[i][0] = height[i]; 63 for (int j = 1; j <= Log[n]; ++j) 64 for (int i = 1; i + (1 << j) - 1 <= n; ++i) 65 f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); 66 } 67 int LCP(int l,int r) { 68 l = rnk[l], r = rnk[r + la + 1]; 69 if (l > r) swap(l, r); 70 l ++; 71 int k = Log[r - l + 1]; 72 return min(f[l][k], f[r - (1 << k) + 1][k]); 73 } 74 void col(int l,int r) { 75 l = max(l, now); 76 r = min(r, lb); // B串中,以now为左端点,右端点在[l,r]之间的串可行的。 77 sum[k - (la - (l - now + 1)) + 1] ++; // l-now+1为B串的长度,la-(l-now+1)为A中的剩余,即需要删除的元素 78 sum[k - (la - (r - now + 1)) + 2] --; 79 } 80 void dfs(int x,int y,int z) { 81 int t = LCP(x, y); 82 x += t, y += t; 83 if (x > la || y > lb) { 84 int R = k - z - (la - x + 1); // 还剩余的修改次数 85 if (R >= 0) col(y - R - 1, y + R - 1); // 这个区间都是可行的 86 return ; 87 } 88 if (z == k) return ; 89 dfs(x + 1, y + 1, z + 1); // 替换 90 dfs(x + 1, y, z + 1); // 删除A中的字符 91 dfs(x, y + 1, z + 1); // 删除B中的字符 92 } 93 int main() { 94 k = read(); 95 scanf("%s%s", a + 1, b + 1); 96 la = strlen(a + 1), lb = strlen(b + 1); 97 n = la + lb + 1; 98 for (int i = 1; i <= la; ++i) s[i] = a[i]; 99 s[la + 1] = '$'; 100 for (int i = 1; i <= lb; ++i) s[i + la + 1] = b[i]; 101 getsa(); 102 getheight(); 103 rmq(); 104 int m = k + k + 1; LL ans = 0; 105 for (now = 1; now <= lb; ++now) { // 计算B的这个后缀中多少前缀是可行的。 106 for (int i = 1; i <= m; ++i) sum[i] = 0; 107 dfs(1, now, 0); 108 for (int i = 1; i <= m; ++i) if (sum[i] += sum[i - 1]) ans ++; 109 } 110 cout << ans; 111 return 0; 112 }
3277: 串
来源:https://www.cnblogs.com/mjtcn/p/10211803.html