洛谷 P1026统计单词个数题解--zhengjun
题目描述
给出一个长度不超过 \(200\) 的由小写英文字母组成的字母串(该字串以每行 \(20\) 个字母的方式输入,且保证每行一定为 \(20\) 个)。要求将此字母串分成 \(k\) 份,且每份中包含的单词个数加起来总数最大。
每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this
中可包含 this
和 is
,选用 this
之后就不能包含 th
。
单词在给出的一个不超过 \(6\) 个单词的字典中。
要求输出最大的个数。
输入格式
每组的第一行有两个正整数 \(p,k\)。 \(p\) 表示字串的行数,\(k\) 表示分为 \(k\) 个部分。
接下来的 \(p\) 行,每行均有 \(20\) 个字符。
再接下来有一个正整数 \(s\),表示字典中单词个数。 接下来的 \(s\) 行,每行均有一个单词。
输出格式
\(1\)个整数,分别对应每组测试数据的相应结果。
输入输出样例
输入 #1 复制
1 3
thisisabookyouareaoh
4
is
a
ok
sab
输出 #1 复制
7
说明/提示
【数据范围】
对于 \(100\%\) 的数据,\(2 \le k \le 40\),\(1 \le s \le 6\)。
【样例解释】
划分方案为 \(this / isabookyoua / reaoh\)
思路
首先,这是一道有点难度的动态规划。
用 \(f_{i,j}\) 表示 从尾巴用了 \(i\) 个字符分了 \(j\) 段的最多有多少个单词。
则转移方程不难想到:
\(f_{i,j}=max(f_{i,j},f_{l,j-1}+sum_{l+1,i})\)
\(sum\) 就是从 \(j\) 到 \(i\) 的单词个数
然后 \(sum\) 就可以在前面预处理暴力算出来
代码
#include<bits/stdc++.h>
using namespace std;
int p,n,m,k,f[210][50],sum[210][210];
string s,a[10];
bool check(int l,int r){
string x=s.substr(l,r-l+1);//这个函数就是返回字符串s从第l个字符开始后面的r-l+1个字符所组成的字符串
for(int i=1;i<=n;i++)
if(x.find(a[i])==0)//这个函数可以返回a[i]在x中最早出现的位置
return 1;
return 0;
}
int main(){
string ch;
s+='0';
cin>>p>>k;
for(int i=1;i<=p;i++){
cin>>ch;
s+=ch;
}
cin>>n;
m=s.length()-1;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=m;i>=1;i--)
for(int j=i;j>=1;j--){
sum[j][i]=sum[j+1][i];
if(check(j,i))
sum[j][i]++;
}
f[0][0]=0;
for(int i=1;i<=k;i++)
f[i][i]=f[i-1][i-1]+sum[i][i];
for(int i=1;i<=m;i++)
f[i][1]=sum[1][i];
for(int i=1;i<=m;i++)
for(int j=1;j<=k&&j<i;j++)
for(int k=j;k<i;k++)
f[i][j]=max(f[i][j],f[k][j-1]+sum[k+1][i]);
printf("%d",f[m][k]);
return 0;
}