[luogu]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
说明
this/isabookyoua/reaoh
看到这题先想到dp,及其转移方程:
f[i][k]=Max{f[j][k-1],s[j+1][i]}(i为位置,k为次数,s为区间单词数)
注意到一个单词被用过后首字母不能再用,所以如果(j,i)区间以j为首存在单词,则:s[j][i]=s[j+1][i]+1 否则s[j][i]=s[j+1][i]
注意边界,f[i][k](i<k不存在,不能由其转移,否则gg,惨痛的教训...)
代码:
1 //2017.11.7 2 //DP 3 #include<iostream> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 int Max(int x,int y){return x>y?x:y;} 8 namespace lys{ 9 char s[256],c[10][256]; 10 int num[256][256],dp[256][50]; 11 int p,K,n; 12 bool exist(int x,int y){ 13 int l=y-x+1; 14 int i,j,k; 15 for(i=0;i<n;i++){ 16 if(strlen(c[i])>l) continue ; 17 k=x; 18 for(j=0;j<strlen(c[i]);j++) if(c[i][j]!=s[k++]) break ; 19 if(j==strlen(c[i])) return true ; 20 } 21 return false ; 22 } 23 int main(){ 24 int i,j,k; 25 scanf("%d%d",&p,&K); 26 for(i=0;i<p;i++) scanf("%s",s+i*20); 27 scanf("%d",&n); 28 for(i=0;i<n;i++) scanf("%s",c[i]); 29 for(i=p*20-1;i>=0;i--) 30 for(j=i;j>=0;j--){ 31 if(exist(j,i)) num[j][i]=num[j+1][i]+1; 32 else num[j][i]=num[j+1][i]; 33 } 34 for(i=0;i<p*20;i++) dp[i][1]=num[0][i]; 35 for(i=0;i<p*20;i++) 36 for(k=2;k<=K;k++) 37 for(j=k-1;j<i;j++) dp[i][k]=Max(dp[i][k],dp[j][k-1]+num[j+1][i]); 38 printf("%d\n",dp[20*p-1][K]); 39 return 0; 40 } 41 } 42 int main(){ 43 lys::main(); 44 return 0; 45 }