2021 ICPC EC Final B. Beautiful String 题解
题意
问给定字符串t
的所有子串中形如"114514"
分割方案之和。其中'1'、'4'、'5'
表示某一字符串,且可重复。
分析(暴力\(n^3\))
记lcp[i][j]
表示后缀i
和j
的最长公共前缀,那么如果lcp[i][j] >= j - i
则说明i
到j
这一段字符串在j
之后重复出现了。可以使用\(n^2\)的dp预处理:lcp[i][j]= s[i]==s[j] ? lcp[i + 1][j + 1] + 1 : 0
。
在\(n^2\)枚举i
和j
判断出lcp[i][j] >= j - i
之后,枚举k
,若lcp[j][k]
比j - i
大,则答案需要加上这一部分lcp[j][k]
减掉j - i
的长度,当然为了保证绿色部分至少存在1个,k
要从j + 3
开始枚举,而且要保证lcp[j][k]
不能超出了k - j - 1
。
代码(TLE)
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
char s[N];
int main() {
int tt;
scanf("%d",&tt);
while(tt--) {
scanf("%s",s+1);
int n = strlen(s + 1);
vector<vector<int>> lcp(n + 2, vector<int>(n + 2));
for (int i = n; i >= 1; i--)
for (int j = n; j >= i; j--)
if (s[i] == s[j]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
long long ans = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
if (lcp[i][j] >= j - i) {
for (int k = j + 3; k <= n; k++) {
if (lcp[j][k] > j - i) {
int mi = min(lcp[j][k], k - j - 1) - (j - i);
ans += max(mi, 0);
}
}
}
printf("%lld\n",ans);
}
}
分析(预处理\(n^2\))
对于上述过程,i、j、k
是同时枚举的,std的优化是先枚举i、j
,将j
对后缀的贡献记录下来,再枚举j、k
。
对于一个合法的i、j
即lcp[i][j] >= j - i
,若存在k
对答案有贡献,那么lcp[j][k]
也至少要是j - i
,换句话说,从j
往后j - i
到整个字符串末尾都有可能会产生贡献。
记sum[j][len]
表示从j
开始长度为len
的贡献,那么若lcp[i][j] >= j - i
,sum[j][j - i]、sum[j][j - i + 1]、...、sum[j][length(s) - 1]、sum[j][length(s)]
都要+1
,当之后枚举j、k
的时候如果满足条件了,我们应该从j - i
开始加到长度min(lcp[j][k], k - j - 1) - 1
这一段的sum
值,也就是sum[j][j - i] + sum[j][j - i + 1] + ... + sum[j][min(lcp[j][k], k - j - 1) - 2]、sum[j][min(lcp[j][k], k - j - 1) - 1]
。
对上述一些操作进行优化:
"sum[j][j - i]、sum[j][j - i + 1]、...、sum[j][length(s) - 1]、sum[j][length(s)]都要+1"
其实是对一段后缀同时加,可以使用差分优化,即sum[j][j - i] + 1、sum[j][length(s) + 1] - 1
,只不过后续根本用不到sum[j][length(s) + 1]
,所以只需要对sum[j][j - i] + 1
,然后求前缀和即可。
"枚举j、k的时候,我们应该从j开始加上长度min(lcp[j][k], k - j - 1) - 1这一段的sum值"
其实是求一段前缀和,而求前缀和的原始数组就是上面差分数组计算出来的数组。
所以只要在枚举i、j
的时候只统计差分数组的贡献,然后求两遍前缀和,就得到了sum
数组的前缀和,这里不妨直接将sum
先作为差分数组使用,在其的基础上做两遍前缀和将其转化为前缀和数组即可。这样就能够在枚举j、k
的时候\(O(1)\)计算贡献了。
由于要复制一份,满足条件的贡献至多不超过原字符串长度的一半,因此sum
第二维开到一半就够了。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
char s[N];
int main() {
int tt;
scanf("%d",&tt);
while(tt--) {
scanf("%s",s+1);
int n = strlen(s + 1);
vector<vector<int>> lcp(n + 2, vector<int>(n + 2));
for (int i = n; i >= 1; i--)
for (int j = n; j >= i; j--)
if (s[i] == s[j]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
long long ans = 0;
vector<vector<int>> sum(n + 1, vector<int>(n / 2 + 2));
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++)
if (lcp[i][j] >= j - i)
sum[j][j - i]++;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n / 2 + 1; j++)
sum[i][j] += sum[i][j - 1];
for (int j = 1; j <= n / 2 + 1; j++)
sum[i][j] += sum[i][j - 1];
}
for (int j = 1; j < n; j++) {
for (int k = j + 1; k <= n; k++) {
int len = min(lcp[j][k], k - j - 1) - 1;
if (len >= 0) ans += sum[j][len];
}
}
printf("%lld\n",ans);
}
}
标签:Beautiful,String,lcp,int,题解,sum,枚举,tt,前缀
From: https://www.cnblogs.com/KIMYON/p/16842332.html