很久没写过这东西了,复习一波。
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
