题目要求形如
AABCAB 的子串的个数。观察到其中有两个
AB,考虑去枚举第一个
AB,那么满足要求的子串就是
AB 前面紧跟着一个它的前缀,在它后面还有一个
AB 不与它相邻。
设第一个
AB 的第一个字符在位置
i,第二个
AB 的第一个字符在位置
j,
l1=∣A∣,l2=∣AB∣,为了保证
A 非空,
l1<l2。
那么前后能匹配的最大长度就是
ma=min{j−i−1,lcpi,j},其中
lcpi,j 表示以
i 开头的后缀和以
j 开头的后缀的最长公共前缀,可以
O(n2) 预处理,
j−i−1 是因为要保证它们不相邻,所以长度最大为这么多。
答案即为:
i∑j∑l2=1∑mal1=1∑l2−1[lcpi−l1,i≥l1]
i,j 是必须要枚举的,考虑优化后面的循环。
将内层循环展开:
l1=1∑ma−1(ma−l1)×[lcpi−l1,i≥l1]
继续展开并整理可得原式是以下项的和:
⋮l1=1:[lcpi−1,i≥1]l1=2:[lcpi−1,i≥1],[lcpi−2,i≥2]l1=3:[lcpi−1,i≥1],[lcpi−2,i≥2],[lcpi−3,i≥3]l1=ma−1:[lcpi−1,i≥1],[lcpi−2,i≥2],[lcpi−3,i≥3],…,[lcpi−ma+1,i≥ma−1]
应该很好想了,发现每一行行内可以前缀和优化,之后再将每一行做前缀和。
具体地,设
fi,j=k=1∑j[lcpi−k,i≥j]gi,j=k=1∑j(j−k+1)×[lcpi−k,i≥k]=k=1∑jfi,k
最后答案为
i∑j∑gi,ma−1,注意越界处理。
时间复杂度
O(n2)。
代码
CPPCI N = 5e3 + 5;
int n, ans, lcp[N][N], f[N][N], g[N][N];
std::string s;
signed main() {
int T = read();
while(T --) {
std::cin >> s, n = s.size(), s = " " + s, ans = 0;
dep(i, n, 1) dep(j, n, i) if(s[i] == s[j]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
rep(i, 1, n) rep(j, 1, n) f[i][j] = i - j >= 0 ? f[i][j - 1] + (lcp[i - j][i] >= j) : f[i][i];
rep(i, 1, n) rep(j, 1, n) g[i][j] = g[i][j - 1] + f[i][j];
rep(i, 2, n) rep(j, i + 3, n - 1) ans += g[i][std::max(1ll, std::min(j - i - 1, lcp[i][j])) - 1];
printl(ans);
rep(i, 1, n) rep(j, 1, n) lcp[i][j] = f[i][j] = g[i][j] = 0;
}
return 0;
}