题目
https://www.luogu.com.cn/problem/P1026
说明
dp[i][j] 表示字符串从开始到第i个位置,分成j段后的最大单词数
sum[i][j] 表示字符串从i到j的最大单词数
AC代码
#include<bits/stdc++.h>
using namespace std;
int p,k,m,n,dp[210][201],sum[210][210];
string s,a[10];
inline int read()//优化读入速度
{
int ssum=0,ch=1;
char c=0;
while(c<'0'||c>'9')
{
if(c=='-') ch=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
ssum=ssum*10+c-'0';
c=getchar();
}
return ch*ssum;
}
bool check(int l,int r)
{
string t=s.substr(l,r-l+1);
for(int i=1;i<=n;i++)
if(t.find(a[i])==0) return true;
return false;
}
void init()
{
string ch;
s+='0';
//让s的下标从1开始,因为动态规划需要留出来0的位置初始化,因此动态规划的下标都是从1开始的,所以要保持一致
p=read();k=read();
for(int i=1;i<=p;i++)
{
cin>>ch;
s+=ch;
}
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]++;
}
//这里说一下为什么是倒序,我们首先需要知道find返回的字符串的位置是其开头的字符位置;
//倒序查询的话,每次多出的字母都是在开头位置,
//我们正好可以以此字母为基准通过find查询字典里是否有符合要求的字符串
//同时可以保证不会重复使用开头字母,因为它是新出现的
//如果正序遍历,新出现的字母在字符串后面,
//以此为基准匹配出来的字符串的开头一定是前面的字符(除非是单字符),无法保证是否被使用过
}
void work()
{
dp[0][0]=0;
for(int i=1;i<=k;i++) dp[i][i]=dp[i-1][i-1]+sum[i][i];//初始化,前k个字母每个作为一段
for(int i=1;i<=m;i++) dp[i][1]=sum[1][i];//初始化不划分的情况(分成1段)
for(int i=1;i<=m;i++)
for(int j=1;j<=k&&j<i;j++)
//j<=k是因为最多分成k段,而j<i是因为j=i已经在第一个初始化中搞定了,当然逻辑上j<=i也没问题
for(int l=j-1;l<i;l++)
//我觉得l其实应该等于j-1而不是j,因为下面的dp[l][j-1]在l=j-1时
//是符合要求的,j-1个字符分成j-1段没有问题,当然两个都能AC,我就是说一下我的看法
dp[i][j]=max(dp[i][j],dp[l][j-1]+sum[l+1][i]);
//l代表新放置的隔断位置,也可以理解为原来分成j段的倒数第二段的最后一个字符,
//因为l+1就代表最后一段的第一个字符;因此 dp[l][j-1]+sum[l+1][i]的意思就是1到l的位置分成j-1段后的
//最多单词数加上剩余的一段(即l+1到 i)的最多单词数
}
int main()
{
init();
work();
cout<<dp[m][k];
return 0;
}
work函数
work函数也可以写成这样:
void work()
{
dp[0][0]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=k&&j<=i;j++)
//j<=k是因为最多分成k段
for(int l=j-1;l<i;l++)
//我觉得l其实应该等于j-1而不是j,因为下面的dp[l][j-1]在l=j-1时
//是符合要求的,j-1个字符分成j-1段没有问题,当然两个都能AC,我就是说一下我的看法
dp[i][j]=max(dp[i][j],dp[l][j-1]+sum[l+1][i]);
//l代表新放置的隔断位置,也可以理解为原来分成j段的倒数第二段的最后一个字符,
//因为l+1就代表最后一段的第一个字符;因此 dp[l][j-1]+sum[l+1][i]的意思就是1到l的位置分成j-1段后的
//最多单词数加上剩余的一段(即l+1到 i)的最多单词数
}
无注释AC代码
#include<bits/stdc++.h>
using namespace std;
int p,k,m,n,dp[201][201],sum[201][201];
string s,str[10];
inline int read()
{
int x=0,ch=1;
char c=0;
while(c<'0'||c>'9') {
if(c=='-') ch=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return ch*x;
}
bool check(int l,int r)
{
string t=s.substr(l,r-l+1);
for(int i=1;i<=n;i++){
if(t.find(str[i])==0) return true;
}
return false;
}
void init()
{
p=read(),k=read();
s+='0';
string t;
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>>str[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<=m;i++)
for(int j=1;j<=k&&j<=i;j++)
for(int l=j-1;l<i;l++)
dp[i][j]=max(dp[i][j],dp[l][j-1]+sum[l+1][i]);
cout<<dp[m][k];
}
int main()
{
init();
work();
}
参考
题解 P1026 【统计单词个数】
noip提高2001 统计单词个数:STL字符串类型及函数使用:dp
题解 P1026 【统计单词个数】