问题描述
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份 (1 <= k <= 40)
输入
第一行有二个正整数(p,k) p表示字串的行数; k表示分为k个部分。 接下来的p行,每行均有20个字符。 再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)接下来的s行,每行均有一个单词。
输出
每行一个整数,分别对应每组测试数据的相应结果。
实现代码
#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
const int Inf = 0x3f3f3f3f;
const int max_len = 2e2 + 5;
int p, k, s, minlen[max_len], len, dp[max_len][40 + 5];
char str[max_len];
int main() {
cin >> p >> k;
for (int i = 0; i < p; i++) cin >> (str + i * 20 + 1);
p *= 20; // 整长
memset(minlen, Inf, sizeof(minlen));
cin >> s;
for (int i = 1; i <= s; i++) {
char tmp[max_len];
cin >> tmp, len = strlen(tmp);
char* pos = strstr(str + 1, tmp); // 子串地址
while (pos) {
minlen[pos - str] = min(minlen[pos - str], len); // 该位置最小的匹配串长度
pos = strstr(pos + 1, tmp); // 保证每个位置都能查询到
}
}
for (int c = 1; c <= k; c++) {
for (int i = c; i <= p; i++) { // 从 c 开始 因为 前面 的都不能分成c份
dp[i][c] = max(dp[i][c], dp[i - 1][c - 1]); // 判断是否应该在这里切一刀
if (minlen[i] != Inf) {
// 从 j 开始切一刀的每个位置都 + 1 个单词数目(因为都会把这个单词切进去), 在 i 位置 切一刀所以 j 最少也是 i + 1
for (int j = max(i + minlen[i] - 1, i + 1); j <= p; j++) dp[j][c] = max(dp[j][c], dp[i][c]) + 1;
if (minlen[i] == 1) ++dp[i][c]; // 如果单词长度为 1, 那本身也要 + 1
}
}
}
cout << dp[p][k] << endl;
return 0;
}