Codeforces 1238D - AB-string

题目链接:Codeforces 1238D

反向考虑,要求好子串的数量,就用总字串的数量 \(n \choose 2\) 里减去坏子串的数量。\(n \choose 2\) 的原因是长度为 \(1\) 的可以不用考虑,它一定是不满足条件的,所以总量是长度大于等于2的所有子串的数量,要减去的是长度大于等于 \(2\) 的所有坏子串的数量。 考虑什么样的串是坏的,对于一个子串 \(t[1\cdots k]\),只要存在一个 \(t[i]\) 不包含于任何一个长度大于二的回文中,那么 \(t[1\cdots k]\) 就是坏的。而实际上,\(t[i]=t[i-1]\)\(t[i]=t[i+1]\) 的时候,显然是存在的,因为至少有一个长度为2的回文,如果 \(t[i] \neq t[i-1]\) 并且 \(t[i] \neq t[i+1]\),那么可以形成长度为 \(3\) 的回文。所以如果存在这样一个 \(t[i]\) 使得 \(t[1\cdots k]\) 是坏的,那么要么 \(i=1\),要么 \(i=k\)

如果我们从 \(t[2]\) 往后能找到一个(最前面一个)\(t[i]=t[1]\),那么说明 \(t[1]\) 一定包含在回文串里,对于 \(t[k]\) 类似。所以坏的串具有这样的形式:

  • ABB...B
  • BAA...A
  • A...AAB
  • B...BBA

所以 \(O(n)\) 统计这些串的个数就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAX_N = 300000 + 5;
LL ans = 0;
int n;
char s[MAX_N];
int main() {
scanf("%d", &n);
scanf("%s", s + 1);
for (int i = 1; i < n; ++i) {
if (s[i] == s[i + 1]) continue;
int last = i;
for (int j = i + 1; j <= n && s[j] != s[i]; ++j) {
--ans;
last = j - 1;
}
i = last;
}
reverse(s + 1, s + n + 1);
for (int i = 1; i < n; ++i) {
if (s[i] == s[i + 1]) continue;
int last = i;
for (int j = i + 2; j <= n && s[j] != s[i]; ++j) {
--ans;
last = j - 1;
}
i = last;
}
ans += 1LL * n * (n - 1) / 2;
printf("%lld\n", ans);
return 0;
}