题目描述
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。
输入描述
第一行为一个正整数(0<n<=5)表示有n组测试数据
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。
输出描述
每行一个整数,分别对应每组测试数据的相应结果。
样例输入
1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
样例输出
7
分析
这道题属于划分型的动态规划,首先要获得不同区间内单词的数目,采用二维数组a进行存储,然后再进行划分。
#include<iostream>
#include<cstring>
using namespace std;
#define N 200 + 10
int a[N][N],dp[N][N];
//a[i][j]表示i到j之间有多少个单词
//dp[i][j]中i表示前i+1(i从0开始)个字符,j表示j次划分
string w[100];
int main()
{
int T;
cin >> T;
int p,K,s;
string t;
while(T--) {
cin >> p >> K;
string tmp;
t = "";
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
for(int i = 0; i < p; i++) {
cin >> tmp;
t += tmp;//把一行一行字符串拼接成一串
}
cin >> s;
for(int i = 0; i < s; i++)
cin >> w[i];
for(int i = 0; i < t.size(); i++)//从i的位置向前找
for(int j = i; j >= 0; j--) {
a[j][i] = a[j + 1][i];
for(int k = 0; k < s; k++) {
if(w[k].size() <= (i - j + 1)) {
int n = 0,m = j;
//判断是否为单词
while(w[k][n] && w[k][n] == t[m]) {
n++;
m++;
}
if(n == w[k].size()) {
a[j][i]++;
break;
}
}
}
}
for(int i = 0; i < t.size(); i++)
dp[i][0] = a[0][i];//前i+1个字符分成一份
for(int i = 0; i < t.size(); i++)
for(int j = 1; j < K; j++)//划分的次数
for(int k = j - 1; k < i; k++)//至少为j份才可以进行j-1次划分
dp[i][j] = max(dp[i][j],dp[k][j - 1] + a[k + 1][i]);
cout << dp[t.size() - 1][K - 1] << endl;//这里K-1是因为划了K-1次
}
return 0;
}