统计单词个数
题目描述
给出一个长度不超过200200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。
要求将此字母串分成k份(1<=k≤40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。
当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。
输入格式
每组的第一行有2个正整数(p,kp,k)
pp表示字串的行数,k表示分为k个部分。
接下来的pp行,每行均有20个字符。
再接下来有1个正整数s,表示字典中单词个数。(1≤s≤6)
接下来的s行,每行均有11个单词。
输出格式
11个整数,分别对应每组测试数据的相应结果。
输入输出样例
1 3 thisisabookyouareaoh 4 is a ok sab
7
说明/提示
this/isabookyoua/reaoh
在做这道题之前,可以先了解以下几个的STL函数。
1. s.substr(x,y) 在s中取出下标从x开始长度为y的字符串(第一个字母下标为0)。
2. s.find(t) 在s中寻找字符串t,找到则返回0,找不到则返回一个极大值。
3. s.length() 返回字符串s的长度。
此外,分析题意,可以明确的判定为是一道区间dp题。
我们记
sum[i][j]为在字符串中q区间[ i , j ]里出现的单词个数。
dp[i][j]为处理完前i个字母,分成j个区间,能得到的单词个数。
那么得到转移方程:
dp[i][j] = max(dp[i][j] , dp[t-1][j-1] + sum[t][i]), t∈(j,i)
因为划分为j-1个区间,最骚需要j-1个字母,所以t从j开始dp。
最困难的转移方程解决后,我们在来想想怎么初始化。
题目要求说一个字母只能是一个单词的开头。
那么我们考虑倒序。
首先,我们从右往左枚举区间右端点。
然后从在右往左枚举区间左端点,先继承上一个左端点的sum,在判断当前的这个左端点是否能作为某个单词的开头。
如果能就+1.
#include<iostream> #include<cstdio> #include<cstring> #include<string> using namespace std; int dp[1000][100],sum[1000][1000]; int p,k,m,len; string s="0",ch,a[1000]; inline bool check(int l,int r){//判断是否有一个单词以l开头。 string x=s.substr(l,r-l+1); for(int i=1;i<=m;i++) if(x.find(a[i])==0) return true; return false; } int main() { int i,j,t; scanf("%d%d",&p,&k); for(i=1;i<=p;i++){ cin>>ch; s+=ch; } scanf("%d",&m); for(i=1;i<=m;i++) cin>>a[i]; len=s.length()-1; for(i=len;i>=1;i--) for(j=i;j>=1;j--){ sum[j][i]=sum[j+1][i];//继承前一个区间 if(check(j,i)) sum[j][i]++; } for(i=1;i<=len;i++) for(j=1;j<=k&&j<=i;j++) for(t=j;t<=i;t++)//dp核心 dp[i][j]=max(dp[i][j],dp[t-1][j-1]+sum[t][i]); printf("%d",dp[len][k]); }