划分型动态规划之统计单词个数
题目描述 Description
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)(管理员注:这里的不能再用指的是位置,不是字母本身。比如thisis可以算做包含2个is)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。
输入描述 Input Description
第一行为一个正整数(0<n<=5)表示有n组测试数据
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。
输出描述 Output Description
每行一个整数,分别对应每组测试数据的相应结果。
样例输入 Sample Input
1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
样例输出 Sample Output
7
解题思路:要解决该问题,首先需要找出在给出的字符串中所有的字串所包含的字典中的单词个数,我们可以用map[i][j]的二维数组记录,map[i][j]表示字符串i到j的单词个数。利用find函数找出map[i][j]的值,当然一个个的找也能找出来,但比较费时,可以先从字符串的末尾找起,一次向前移动,如果移动的字符串构成一个新单词就加一,否则就为原来的数目。代码如下:
void findwd() { int i,j,m; bool flag; memset(map,0,sizeof(map)); for(i=a.length()-1;i>=0;i--) { for(j=i;j>=0;j--) { for(m=0,flag=0;m<s;m++) { if(a.find(e[m],j)==j&&j+e[m].size()<=i+1) { flag=1; break; } } if(flag) map[j][i]=map[j+1][i]+1; else map[j][i]=map[j+1][i]; } } }
注:position=string.find(str,position1),position是str首字母出现位置,position1是从string的什么位置开始查找。
找出了对应的map[i][j],在利用划分型动态规划,对字符串进行划分,找出最优解。
代码如下:
int solve() { int i,j,m; for(i=0;i<a.size();i++) dp[i][1]=map[0][i]; for(m=2;m<=k;m++) { for(i=m;i<a.size();i++) { for(j=i-1;j>m-1;j--) dp[i][m]=max(dp[i][m],dp[j][m-1]+map[j+1][i]); } } return dp[a.size()-1][k]; }
全部代码:
#include<iostream> #include<string> #include<cstring> using namespace std; string a,e[6]; const int maxn=205,maxnn=45; int map[maxn][maxn],dp[maxn][maxnn]; int k,s,len[6]; void findwd() { int i,j,m; bool flag; memset(map,0,sizeof(map)); for(i=a.length()-1;i>=0;i--) { for(j=i;j>=0;j--) { for(m=0,flag=0;m<s;m++) { if(a.find(e[m],j)==j&&j+e[m].size()<=i+1) { flag=1; break; } } if(flag) map[j][i]=map[j+1][i]+1; else map[j][i]=map[j+1][i]; } } } int solve() { int i,j,m; for(i=0;i<a.size();i++) dp[i][1]=map[0][i]; for(m=2;m<=k;m++) { for(i=m;i<a.size();i++) { for(j=i-1;j>m-1;j--) dp[i][m]=max(dp[i][m],dp[j][m-1]+map[j+1][i]); } } return dp[a.size()-1][k]; } int main() { int t,p; cin>>t; while(t--&&cin>>p>>k) { a.clear(); string b; while(p--&&cin>>b) a+=b; cin>>s; for(int i=0;i<s;i++) { cin>>e[i]; len[i]=e[i].length(); } findwd(); cout<<solve()<<endl; } return 0; }
dp[i][m]表示从零到i的m次划分的单词个数。