划分型动态规划 之 CODE[VS] 1040 统计单词个数 2001年NOIP全国联赛提高组
/*
dp[i][k] := 前i+1个字符组成的字符串,划分为k份,每份中包含的单词个数加起来总数的最大值。
初始化:
dp[][] = -0x3f3f3f3f
// 注意:对于dp[i][k],若(i+1)<k,则dp[i][k]的值不存在,设为-0x3f3f3f3f。例如:dp[0][2] = -0x3f3f3f3f
dp[i][1] = val[0][i]
//注:val[i][j] := 截取原字符串【从i到j的字符(包含i和j位置的字符)】组成字符串,返回其包含的单词个数
状态方程:
dp[i][k] = max(dp[i][k], dp[j][k-1]) + Num(j+1, i))
(0<=j<i)
答案:
dp[len-1][k]
此题难点:求val[i][j]
val[i][j] = val[i][j-1] + num(j)
num(j) := 遍历所有想要匹配的字符串strArr[m],从原字符串strS位置j出发向前找字符,找到一个首字符位置fst,
使得strS[fst,j]的长度刚好等于strArr[m],若满足以下条件:
1)i<=fst<=j
对应代码:(j - i + 1) >= strArr[m].size()
2)此字符串首字符的位置fst在求val[i][j-1]和求num(j)时,没有用过
对应代码:!used[j - strArr[m].size() + 1]
3)strS[i,j] == strArr[m]
对应代码:strSource.substr(j - strArr[m].size() + 1, strArr[m].size()) == strArr[m]
则:+1
最后找到几个满足这三个条件的首位置fst,num(j)的值就是几。
*/
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstddef> 5 #include <iterator> 6 #include <algorithm> 7 #include <string> 8 #include <locale> 9 #include <cmath> 10 #include <vector> 11 #include <cstring> 12 using namespace std; 13 const int INF = -0x3f3f3f3f; 14 const int MaxN = 210; 15 const int MaxK = 50; 16 17 int dp[MaxN][MaxK]; 18 int val[MaxN][MaxN]; 19 int len, K, S; 20 string strSource; 21 string strArr[10]; 22 23 24 void iniVal() 25 { 26 bool used[MaxN]; 27 for (int i = 0; i < len; ++i) 28 { 29 memset(used, 0, sizeof(used)); 30 for (int j = i; j < len; ++j) 31 { 32 if (i != j) val[i][j] = val[i][j - 1]; 33 for (int m = 0; m < S; ++m) 34 { 35 if ((j - i + 1 >= strArr[m].size()) && !used[j - strArr[m].size() + 1] 36 && (strSource.substr(j - strArr[m].size() + 1, strArr[m].size()) == strArr[m])) 37 { 38 ++val[i][j]; 39 used[j - strArr[m].size() + 1] = true; 40 } 41 } 42 } 43 } 44 } 45 46 void Solve() 47 { 48 for (int i = 0; i < len; ++i) 49 { 50 dp[i][1] = val[0][i]; 51 } 52 for (int i = 0; i < len; ++i) 53 { 54 for (int j = 0; j < i; ++j) 55 { 56 for (int k = 2; k <= K; ++k) 57 { 58 dp[i][k] = max(dp[i][k], dp[j][k - 1] + val[j+1][i]); 59 int bbb = 1; 60 } 61 } 62 } 63 cout << dp[len-1][K] << endl; 64 65 66 67 /*cout << "\n\n\n\n" << "val:----------------------------" << endl; 68 for (int i = 0; i < len; ++i) 69 { 70 for (int j = 0; j < len; ++j) 71 { 72 cout << val[i][j] << " "; 73 } 74 cout << endl; 75 } 76 77 cout << "\n\n\n\n" << "dp:----------------------------" << endl; 78 for (int i = 0; i < len; ++i) 79 { 80 for (int j = 1; j <= K; ++j) 81 { 82 cout << dp[i][j] << " "; 83 } 84 cout << endl; 85 }*/ 86 } 87 88 int main() 89 { 90 #ifdef HOME 91 freopen("in", "r", stdin); 92 //freopen("out", "w", stdout); 93 #endif 94 95 int cas; 96 cin >> cas; 97 while (cas--) 98 { 99 strSource = ""; 100 memset(val, 0, sizeof(val)); 101 for (int i = 0; i < MaxN; ++i) 102 { 103 for (int j = 0; j < MaxK; ++j) 104 { 105 dp[i][j] = INF; 106 } 107 } 108 int p; 109 cin >> p >> K; 110 string strTmp; 111 for (int i = 0; i < p; ++i) 112 { 113 cin >> strTmp; 114 strSource += strTmp; 115 } 116 len = strSource.size(); 117 cin >> S; 118 for (int i = 0; i < S; ++i) 119 { 120 cin >> strArr[i]; 121 } 122 iniVal(); 123 Solve(); 124 } 125 126 #ifdef HOME 127 cerr << "Time elapsed: " << clock() / CLOCKS_PER_SEC << " ms" << endl; 128 _CrtDumpMemoryLeaks(); 129 system("pause"); 130 #endif 131 return 0; 132 }