给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)(管理员注:这里的不能再用指的是位置,不是字母本身。比如thisis可以算做包含2个is)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。
第一行为一个正整数(0<n<=5)表示有n组测试数据
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。
每行一个整数,分别对应每组测试数据的相应结果。
1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
7
类型:dp 难度:2this/isabookyoua/reaoh
题意:给出一个字符串,以及一个有s个词的字典,将这个字符串分成k份,求如何分词总数最大。字符串中,两个词不能从同一个位置开启,但是可以在同一个位置结束。即this和th只能算一个,但是this和is算两个。
分析:首先需要统计字符串中词的个数和位置,我的方法是,用len[i]记录从i开始的词的最小长度(因为两个词不能从一个位置开始,所以从一个位置开始的词只能取一个,显然,取的词越短越不会受划分的影响,越容易得到更多的词)。
然后,用划分dp解决,用dp[pos][k]表示将字符串从pos位置开始到结尾这段子串,划分成k份能得到的最大词数。
递推方程:dp[pos][k] = mmax(cnt+dp[i+1][k-1]),其中i为本次划分的位置,cnt为[pos,i]这段子串包含的单词数,dp[0][k]
代码如下:
#include<iostream> #include<cstring> #include<string> using namespace std; int len[210],dp[210][210]; string sen; int mmin(int a,int b) { if(a<0) return b; if(b<0) return a; return (a<b)?a:b; } int mmax(int a,int b) { return (a>b)?a:b; } int fun(int pos,int k) { if(pos<0 || k<=0) return 0; if(dp[pos][k]>0) return dp[pos][k]; int endcnt[210],cnt = 0; memset(endcnt,0,sizeof(endcnt)); for(int i=pos; i<=sen.length()-k; i++) { if(len[i]>0) endcnt[i+len[i]-1]++; cnt += endcnt[i]; dp[pos][k] = mmax(dp[pos][k],cnt+fun(i+1,k-1)); } return dp[pos][k]; } int main() { int t; cin>>t; while(t--) { memset(len,-1,sizeof(len)); memset(dp,0,sizeof(dp)); int p,k; cin>>p>>k; string tmp; while(p--) { cin>>tmp; sen += tmp; } int s; cin>>s; while(s--) { string word; cin>>word; for(int i=0; i<=sen.length()-word.length(); i++) { string sub(sen.substr(i,word.length())); if(word == sub) len[i] = mmin(len[i],word.length()); } } cout<<fun(0,k)<<endl; } }