建议访问原文出处,获得更佳浏览体验。
原文出处:https://hyp1231.github.io/2018/07/10/20180710-hdu5677/
题意
给出
个字符串,所有字符串的连续回文子串构成一个多重集合
求
中是否存在恰好
个串,使它们的长度之和为
链接
题解
只与每个串的长度有关,故应先预处理出每个长度的回文串的数量。
使用 Manacher 算法计算出以串的各个位置为中心的最大回文串长度。若以 为中心的最大回文串的长度为 (不妨假设它为偶数),考虑逐步去掉两侧字符就可以造出更多回文子串。因此长度为 的子串的计数器分别加一(若 为奇数,同理)。这样我们就得到了每个长度的串的数量,
考虑选出
个串使它们长度之和为
。即选出
个物品,填满容量为
的背包,每种物品的个数已知,是多重背包问题。设计
表示当前已经选了
个串,放在容量为
的背包中的最大容量。记放入新串后数量为
,当前背包容量为
,新增串个数为
,新增子串长
,则状态转移方程为:
Hint 1: 转移要求在 (第一次添加串)或 不为 时才转移(说明存在上一个状态)。
Hint 2: 使用二进制优化加速多重背包。
记长度为 的回文子串的数量为 。读入和 次 Manacher 是 的,多重背包是 。总时间复杂度 。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 128;
const char mid_c = '$';
const char side_c = '@';
int n, k, l;
char s[N][N];
char s2[N << 1];
int len, r[N][N << 1];
void prepare(char s1[]) {
len = 0;
s2[len++] = side_c;
s2[len++] = mid_c;
int n = strlen(s1);
for (int i = 0; i < n; ++i) {
s2[len++] = s1[i];
s2[len++] = mid_c;
}
s2[len + 1] = '\0';
}
void Manacher(char s1[], int id) {
prepare(s1);
memset(r[id], 0, sizeof(r[id]));
int mid = 0, p = 0;
for (int i = 0; i < len; ++i) {
int x;
if (p < i)x = 1;
else x = std::min(p - i, r[id][2 * mid - i]);
while (s2[i - x] == s2[i + x]) ++x;
if (i + x > p) {
p = i + x;
mid = i;
}
r[id][i] = x;
}
}
int cnt[N], dp[N][N];
// dp[i][j] 代表 i 个子串拼接成的串的最大长度(不超过 j)
int main() {
int T;
scanf("%d", &T);
while (T--) {
memset(cnt, 0, sizeof(cnt));
memset(dp, 0, sizeof(dp));
scanf("%d%d%d", &n, &k, &l);
for (int i = 0; i < n; ++i) {
scanf("%s", s[i]);
}
for (int i = 0; i < n; ++i) {
Manacher(s[i], i);
for (int j = 0; j < len; ++j) {
int tmp = r[i][j] - 1;
if (tmp <= 0) continue;
while (tmp > 0) { // 最长子串逐步去掉侧边字母也是子串
++cnt[tmp];
tmp -= 2;
}
}
}
for (int i = 0; i <= l; ++i) { // 枚举子串长度
int sum = cnt[i]; // 长度为 i 的子串数量
for (int j = 1; sum; j <<= 1) { // 二进制优化
int mul = std::min(sum, j);
for (int p = l; p >= mul * i; --p) // 枚举长度
for (int num = mul; num <= k; ++num)// 枚举已选字符串个数
if (num == mul || dp[num - mul][p - mul * i] > 0)
// 如果本串是第一次被选,或存在选了 p - mul * i 个串的方案
dp[num][p] = std::max(dp[num][p],
dp[num - mul][p - mul * i] + mul * i);
sum -= mul;
}
}
printf("%s\n", (dp[k][l] == l ? "True" : "False"));
}
return 0;
}