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 }

 

posted @ 2019-04-05 10:53  IAT14  阅读(173)  评论(0编辑  收藏  举报