题目:https://www.luogu.org/problemnew/show/P1026
题意:
给定一个字符串,要求把他分成k段。给定s个单词,问划分成k段之后每段中包含的单词和最大是多少。
一个位置作为单词的开头只能计算一次。
思路:
如果仅仅是统计某一个区间内的最大单词数,这比较简单。每次从后往前推一个字符,如果这个字符是一个单词的开头,那么$cnt[i][j] = cnt[i+1][j]+1$
但是现在要分成$j$段,这是状态之一,另一个状态应该是已经考虑前$i$个字符。
也就是用$dp[i][j]$表示前$i$个字符划分成$j$段的最大单词数。
很显然$dp[i][j] = dp[d][j-1] + cnt[d +1][i]$,也就是说在$0~i$之间找到一个位置$d$,从这里断开,前面的组成$j-1$段,后面的作为一个新的段。
需要考虑的是,一个只有$i$个字符的字符串,最多只能划分成$i$段。
并且要注意当$j =1$时需要特殊处理。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<map>
4 #include<set>
5 #include<cstring>
6 #include<algorithm>
7 #include<vector>
8 #include<cmath>
9 #include<stack>
10 #include<queue>
11 #include<iostream>
12
13 #define inf 0x7fffffff
14 using namespace std;
15 typedef long long LL;
16 typedef pair<string, string> pr;
17
18 int p, k, s;
19 const int maxn = 205;
20 string str;
21 string word[10];
22 int cnt[maxn][maxn];
23 int dp[maxn][maxn];
24
25 int main()
26 {
27 scanf("%d%d", &p, &k);
28 for(int i = 0; i < p; i++){
29 string tmp;
30 cin>>tmp;
31 str += tmp;
32 }
33 scanf("%d", &s);
34 for(int i = 1; i <= s; i++){
35 cin>>word[i];
36 }
37
38 int len = str.length();
39
40 for(int i = len - 1; i >= 0; i--){
41 for(int j = i; j >= 0; j--){
42 for(int x = 1; x <= s; x++){
43 int l = word[x].length();
44 if(str.substr(j, min(i - j + 1, l)) == word[x]){
45 cnt[j][i] = cnt[j + 1][i] + 1;
46 break;
47 }
48 else cnt[j][i] = cnt[j + 1][i];
49 //cout<<str.substr(j, min(i - j + 1, l))<<" "<<cnt[j][i]<<endl;
50 }
51
52 }
53 }
54
55 for(int i = 1; i <= k; i++){
56 dp[i][i] = dp[i - 1][i - 1] + cnt[i][i];
57 }
58 for(int i = 0; i < len; i++){
59 dp[i][1] = cnt[0][i];
60 for(int j = 2; j <= min(k, i); j++){
61 for(int d = j - 1; d < i; d++){
62 dp[i][j] = max(dp[i][j], dp[d][j - 1] + cnt[d + 1][i]);
63 }
64 }
65 }
66
67 printf("%d\n", dp[len - 1][k]);
68
69 return 0;
70 }
来源:https://www.cnblogs.com/wyboooo/p/10851238.html