洛谷 P1026 统计单词个数 区间DP
题目描述
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。
输入输出格式
输入格式:
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。
输出格式:
一个整数,分别对应每组测试数据的相应结果。
输入输出样例
输入样例#1:
1 3 thisisabookyouareaoh 4 is a ok sab
输出样例#1:
7
方法挺直接的,先求出每一段字符串包含了多少单词,再求出将整个字符串分割成 m 段最多可以获得多少单词。
dp[i][j] 表示前 i 个字符组成的串,分割成 j 段,最多能获得多少单词。
dp[i][j] = max(dp[k][j-1] + wordNum[k+1][length]) (j-1 <= k <= length-1)
代码:
#include <iostream> #include <cstring> using namespace std; const int MAX = 205; const int INF = 0x3fffffff; int wordNum[MAX][MAX]; //i 到 j 这一段包括了多少个单词 int dp[MAX][MAX]; //剩下的 i 串,分成 j 段最多包含多少单词 string s; string words[10]; int l, m, n; int dfs(int i, int j); //枚举 int main(){ // freopen("input.txt", "r", stdin); cin >> l >> m; s = ""; while(l--){ string t; cin >> t; s += t; } cin >> n; for(int i=1; i<=n; i++){ cin >> words[i]; } //求出 dp 数组 memset(wordNum, 0, sizeof(wordNum)); for(int len = 1; len <=s.length(); len++){ for(int i=0; i+len-1<s.length(); i++){ int j = i + len - 1; bool hasWord = false; for(int x=1; x<=n; x++){ //从最左边的字符开始能不能构成一个单词 string t = words[x]; int p = i, q = 0; while(p <= j && q < words[x].length()){ if(s[p] != words[x][q]) break; p++; q++; } if(q >= words[x].length()){ hasWord = true; break; } } wordNum[i][j] = wordNum[i+1][j] + hasWord; //能够成就加一 } } memset(dp, 0, sizeof(dp)); for(int i=0; i<s.length(); i++){ dp[i][1] = wordNum[0][i]; } for(int j=2; j<=m; j++){ //分割成 j 段 for(int i=0; i<s.length(); i++){ //有 i 个字符 for(int k=j-2; k<i; k++){ //分割点 dp[i][j] = max(dp[i][j], dp[k][j-1] + wordNum[k+1][i]); } } } cout << dp[s.length()-1][m]; return 0; }