luogu P1026 统计单词个数 序列DP
dp[i][k]表示,在i及i左侧,分成k块,的最大单词数目。
w[i][j]表示,在[i,j],内部多少个单词在此区间开始和结束。
转移方程为dp[i][k] = max(dp[j][k - 1] + w[j + 1][i])
通过考虑每个单词的结束而非开始,而避开后后效性问题。
如果某个单词是另一个单词的前缀,则另一个单词无需考虑。
字符串处理的有问题,调了挺久的....
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 int n,k,len,tot,root,p; 6 int dp[210][50],w[210][210],ch[1000][30]; 7 char s[210],str[210]; 8 bool end[1000]; 9 void add(char *voc) 10 { 11 int l = strlen(voc + 1),u = root; 12 for (int i = 1;i <= l;i++) 13 { 14 if (end[u] == true) 15 break; 16 if (ch[u][voc[i] - 'a'] != 0) 17 u = ch[u][voc[i] - 'a']; 18 else 19 u = ch[u][voc[i] - 'a'] = ++tot; 20 } 21 end[u] = true; 22 } 23 int main() 24 { 25 root = tot = 1; 26 scanf("%d%d",&n,&k); 27 for (int i = 1;i <= n;i++) 28 scanf("%s",s + (i - 1) * 20 + 1); 29 len = strlen(s + 1); 30 scanf("%d",&p); 31 for (int i = 1;i <= p;i++) 32 { 33 scanf("%s",str + 1); 34 add(str); 35 } 36 for (int i = 1;i <= len;i++) 37 { 38 for (int j = i;j <= len;j++) 39 { 40 w[i][j] += w[i][j - 1]; 41 int u = root,t = j; 42 while (u != 0) 43 { 44 u = ch[u][s[t] - 'a']; 45 if (end[u] == true) 46 { 47 w[i][t]++; 48 break; 49 } 50 t++; 51 } 52 } 53 } 54 for (int i = 1;i <= len;i++) 55 dp[i][1] = w[1][i]; 56 for (int o = 2;o <= k;o++) 57 for (int i = o;i <= len;i++) 58 for (int j = o - 1;j <= i - 1;j++) 59 dp[i][o] = max(dp[j][o - 1] + w[j + 1][i],dp[i][o]); 60 printf("%d",dp[len][k]); 61 }
心之所动 且就随缘去吧