题目描述
给出一个长度不超过200200的由小写英文字母组成的字母串(约定;该字串以每行2020个字母的方式输入,且保证每行一定为2020个)。要求将此字母串分成kk份(1<k \le 401<k≤40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串thisthis中可包含thisthis和isis,选用thisthis之后就不能包含thth)。
单词在给出的一个不超过66个单词的字典中。
要求输出最大的个数。
输入格式
每组的第一行有22个正整数(p,kp,k)
pp表示字串的行数,kk表示分为kk个部分。
接下来的pp行,每行均有2020个字符。
再接下来有11个正整数ss,表示字典中单词个数。(1 \le s \le 61≤s≤6)
接下来的ss行,每行均有11个单词。
输出格式
11个整数,分别对应每组测试数据的相应结果。
输入输出样例
1 3 thisisabookyouareaoh 4 is a ok sab
7
说明/提示
this/isabookyoua/reaoh
预备知识:
c++中string类型的用法:
1.x.substr(a,len) 返回x字符串中下标从a开始,长度为len的字符串
2.s.lenth()计算s字符串的长度
3.s.find(a)在s字符串中寻找a字符串(也就是判断a是否是s的子串)若a是s的子串,则会返回a的首字母在s中的下标(因为s相当于是字符数组)
4.详细用法:https://blog.csdn.net/qq_37941471/article/details/82107077
https://www.cnblogs.com/Draymonder/p/6944479.html
https://blog.csdn.net/Alexander_1314/article/details/82192687
考虑思路:
设dp[i][j]表示从1->i分割成j段的最大单词个数
首先,预处理出原字符串任意两点间能统计的单词个数,然后以终点为阶段在最外层枚举,然后枚举次级阶段:分的段数,注意分段存在限制:段数小于等于字母个数,并且小于最大
允许分段数,最内层枚举断点,范围从1->i-1,进行区间dp
状态转移方程:dp[i][j]=max(dp[i][j],dp[k][j-1]+sum[k+1][i]);
AC代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int p,k,m,n;
int dp[1200][1200];
int sum[1200][1200];
inline int read()
{
char ch;
while((ch=getchar())<'0'|| ch>'9');
int res=ch-48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+ch-48;
return res;
}
string s,a[10];
bool check(int l,int r)
{
string x=s.substr(l,r-l+1);
for(int i=1;i<=n;i++)
if(x.find(a[i])==0)
return true;
return false;
}
void init()
{
p=read();
k=read();
string t;
s+='0';
for(int i=1;i<=p;i++)
{
cin>>t;
s+=t;
}
n=read();
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]++;
}
}
void work()
{
for(int i=1;i<=k;i++)
dp[i][i]=dp[i-1][i-1]+sum[i][i];
for(int i=1;i<=m;i++)
dp[i][1]=sum[1][i];
for(int i=1;i<=m;i++)
for(int j=1;j<=k&&j<=i;j++)
{
for(int q=j;q<i;q++)
dp[i][j]=max(dp[i][j],dp[q][j-1]+sum[q+1][i]);
}
cout<<dp[m][k];
}
int main()
{
init();
work();
return 0;
}