满分做法:
用(dp[i][j])表示到(i)位置分为(j)段的最大值,区间dp通常枚举断点,所以预处理(sum[i][j])表示区间([i,j])的单词数。
本题还用了string类型的小技巧,推荐做一下。
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdio>
using namespace std;
int p,k;
string s;
int dp[210][55];//dp[i][j]表示到i分为j段的最单词数
int sum[210][210];
int n;
string a[8];
bool check(int l,int r)//寻找以l开头的字符串
{
string x=s.substr(l,r-l+1);//裁剪
for(int i=1;i<=n;i++)
if(x.find(a[i])==0) return 1;//直接找字符串,返回开头位置
return 0;
}
int main()
{
scanf("%d%d",&p,&k);
s+='$';//下表从1开始
for(int i=1;i<=p;i++)
{
string x;
cin>>x;
s+=x;
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=p*20;i++)//枚举结尾
{
for(int j=i;j>=1;j--)//倒叙处理转移
{
sum[j][i]=sum[j+1][i];
if(check(j,i))
sum[j][i]++;
}
}
for(int j=1;j<=k;j++)
{
for(int i=1;i<=p*20;i++)
{
for(int q=j-1;q<i;q++)//枚举断点
{
dp[i][j]=max(dp[i][j],dp[q][j-1]+sum[q+1][i]);
}
}
}
printf("%d
",dp[p*20][k]);
return 0;
}