题目描述 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
数据范围及提示 Data Size & Hint
类型: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;
}
}

本文介绍了一个算法,用于将一个由小写英文字母组成的字符串分割成k份,使得每份中包含的单词个数加起来最多。算法通过统计字符串中的单词个数和位置,使用动态规划(dp)解决此问题。

669

被折叠的 条评论
为什么被折叠?



