专栏文章
题解:P1026 [NOIP 2001 提高组] 统计单词个数
P1026题解参与者 5已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @mipohx4r
- 此快照首次捕获于
- 2025/12/03 15:21 3 个月前
- 此快照最后确认于
- 2025/12/03 15:21 3 个月前
废话不多说,多说的不是废话,我们直接进入正题。
题目简化: 没什么好简化的,自己看题目去。
首先,让我们来回顾一下区间 DP 的知识。
区间 DP,即对一个区间内的元素进行合并或拆分的操作,在满足要求的前提下求出最小(或最大)代价。
好,让我们回到这道题上。题目中的“分割成 个部分”这句话,看上去不就很像一个区间 DP 吗?因为这是分割型 DP(具体定义详见这篇文章),所以我们定义的 DP 状态 表示前 个字符分割成 个区间的最大个数,那么状态转移方程就可以写成这样:
其中 表示从第 个字符到第 个字符中的单词数。
现在我们就需要考虑一下这个 怎么求了。
我这里提供一种思路:我们可以把 也当做一个 DP,那么 首先就肯定要继承上一个状态,即 ,然后我们看看当前从 到 这个字符串中有没有单词。
但我相信很多人都能看出来其中的问题:这个字符串里的同一个单词在上次和这次会被重复计算。我又想到了一个办法:我只需要找后几个字母构成的“词”是不是单词就行了,而这个“词”的长度就是单词的长度。
代码:
CPP#include<bits/stdc++.h>
#define int long long
#define npos string::npos
using namespace std;
int n,m,kk,vis[206],siz[16],a[206][206],dp[206][46];
string s,ss[16];
signed main()
{
s+=" ";
cin>>m>>kk;
while(m--)
{
string t="";
cin>>t;
s+=t;
}
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>ss[i];
siz[i]=ss[i].size();
}
n=s.size()-1;//这里注意!!!
for(int i=1;i<=n;i++)
{
string t="";
memset(vis,0,sizeof(vis));
for(int j=i;j<=n;j++)
{
a[i][j]=a[i][j-1];
t+=s[j];
int l=t.size();
for(int k=1;k<=m;k++)
{
if(l-siz[k]<0||vis[l-siz[k]])
{
continue;
}
if(t.rfind(ss[k])==l-siz[k])//详见上面的文章
{
a[i][j]++;
vis[l-siz[k]]=1;
}
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=min(i,kk);j++)
{
for(int k=j;k<=i;k++)
{
dp[i][j]=max(dp[i][j],dp[k-1][j-1]+a[k][i]);
}
}
}
cout<<dp[n][kk];
return 0;
}
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...