统计单词个数
题目描述
给出一个长度不超过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
思路:
1,把很多行的串合并为一个串
2,扫描串中所有的首字母,如果构成单词,把单词的结尾加入数组d中,那么 i,d[i] 就代表了一个单词的头尾
3,从第一个字符开始进行递归的划分,直到只剩一次划分机会,就把剩下的所有字符都用上
4,统计各段的单词之和,更新最大值(也就是枚举了所有的划分情况)
统计各段单词利用数组d来完成,即检查以某个字母开头的单词结尾是否在这个段中,计算过的段结果保存起来,记忆化搜索
下面上代码:
#include<algorithm>
using namespace std;
#include<cstring>
#include<vector>
#include<cstdio>
int p,k,s,e;
char a[256];
int d[256]={0};
int dp[256][256]={0};
struct Word{
char s[128]; //单词信息
int len; //单词长度
};
Word w[6];
//判断以i开头到r之间的串是否和单词j匹配
bool match(int i,int r,int j){
if(r-i+1<w[j].len) return 0; //剩余长度小于单词长度
for(int x=0;x<w[j].len;x++){
if(a[i+x]!=*(w[j].s+x)) return 0;
}
return 1;
}
//统计l,r之间的单词数目,构造d[256]数组
int calc(int l,int r){
int sum=0;
for(int i=l;i<=r;i++){
for(int j=0;j<s;j++){
//对于同一个首字母,尽可能选取较短的单词进行匹配
if(d[i]-i+1>w[j].len&&match(i,r,j)==1){
d[i]=i+w[j].len-1; //d[i]保存以i开头的单词的末尾
//sum++;
}
}
}
return sum;
}
int fun(int l,int r){
if(dp[l][r]) return dp[l][r];
int sum=0;
for(int i=l;i<=r;i++){
if(d[i]<=r) sum++;
}
return dp[l][r]=sum;
}
int wi=0,MAX=0;
//s为开始点,k为剩余段数
void dfs(int s,int k){
//只剩一次,全部分完
int temp;
if(k==1){
temp=fun(s,e);
wi+=temp;
MAX=max(MAX,wi);
wi-=temp;
return;
}
for(int i=s;i<=e-k+1;i++){
temp=fun(s,i);
wi+=temp;
dfs(i+1,k-1);
wi-=temp;
}
}
int main(){
memset(d,127,sizeof(d)); //d[256]初始化为很大的常数
scanf("%d%d",&p,&k);
for(int i=0;i<p;i++){
scanf("%s",&a[i*20]);
}
scanf("%d",&s);
for(int i=0;i<s;i++){
scanf("%s",w[i].s);
w[i].len=strlen(w[i].s);
}
e=strlen(a)-1;
calc(0,e);
dfs(0,k);
printf("%d\n",MAX);
return 0;
}
青,取之于蓝而青于蓝。冰,水为之而寒于水。