题目描述 Description
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)(管理员注:这里的不能再用指的是位置,不是字母本身。比如thisis可以算做包含2个is)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。
输入描述 Input Description
第一行为一个正整数(0<n<=5)表示有n组测试数据
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。
输出描述 Output Description
每行一个整数,分别对应每组测试数据的相应结果。
样例输入 Sample Input
1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
样例输出 Sample Output
7
数据范围及提示 Data Size & Hint
this/isabookyoua/reaoh
这是一个划分型动态规划的题目。题目的意思是,将一个最长为200的字符串进行划分,划分成为k部分,给出p个单词,要求这k个部分里面单词出现次数最多,但是有一点作为要求是比如this,如果他算上this这个单词,但是如果有this了就不能是t或者是th或者thi,只能是is或者s或者his,也就是子串的第一个字母不能出现多次。
那么对于这个题,要是进行划分的话,通过前几次我们对划分型DP的理解,一般形式就是从i划分j份就是t划分j-1份再加上从t+1到i满足条件的个数这里面的最优解。
对于这个题,我们需要新建一个数组来存从i到j的单词的个数,我们选择从后往前遍历,因为这样就可以避免题目中说的子串第一个字母不能出现多次的问题。这个比较好实现,利用STL里string.find()较为轻松得出。
动态转移方程:
check[i][j] = check[i + 1][j] i到j有子串
check[i][j] = check[i + 1][j] i到j无子串
dp[i][j] = max(dp[i][j], dp[t][j - 1] + check[t + 1][i]) j是划分的个数,j - 1 <= t < i < str.size() - 1
具体见代码:
/*************************************************************************
> File Name: 统计单词个数.cpp
> Author: Zhanghaoran0
> Mail: chiluamnxi@gmail.com
> Created Time: 2015年07月25日 星期六 09时00分12秒
************************************************************************/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int n, p, k, s;
string str;
string word[7];
int check[201][201];
int dp[201][201];
void DP();
void work(){
bool flag;
for(int j = str.size() - 1; j >= 0; j --){
for(int i = j; i >= 0; i --){
for(int l = 1; l <= s; l ++){
flag = false;
if(str.find(word[l], i) == i && word[l].size() <= j - i + 1){ //str.find(s, i) 从str这个串的第i个下标开始,寻找子字符串s,如果找到,返回s在str中的坐标。
flag = true;
break;
}
}
if(flag){
check[i][j] = check[i + 1][j] + 1;
}
else
check[i][j] = check[i + 1][j];
}
}
DP();
}
void DP(){
for(int i = 0; i < str.size(); i ++){
dp[i][1] = check[0][i];
}
for(int j = 2; j <= k; j ++){
for(int i = j - 1; i < str.size(); i ++){
for(int t = i - 1; t >= j - 1; t --){
dp[i][j] = max(dp[i][j], dp[t][j - 1] + check[t + 1][i]);
}
}
}
}
int main(void){
freopen("in.txt", "r", stdin);
string temp;
memset(check, 0, sizeof(check));
memset(dp, 0, sizeof(dp));
cin >> n;
while(n --){
cin >> p >> k;
str = "";
while(p --){
cin >> temp;
str += temp;
}
cin >> s;
for(int i = 1; i <= s; i ++){
cin >> word[i];
}
work();
cout << dp[str.size() - 1][k] << endl;
}
return 0;
}