题解:P1026 [NOIP 2001 提高组] 统计单词个数
废话不多说,多说的不是废话,我们直接进入正题。
题目简化: 没什么好简化的,自己看题目去。
首先,让我们来回顾一下区间 DP 的知识。
区间 DP,即对一个区间内的元素进行合并或拆分的操作,在满足要求的前提下求出最小(或最大)代价。
好,让我们回到这道题上。题目中的“分割成
其中
现在我们就需要考虑一下这个
我这里提供一种思路:我们可以把
但我相信很多人都能看出来其中的问题:这个字符串里的同一个单词在上次和这次会被重复计算。我又想到了一个办法:我只需要找后几个字母构成的“词”是不是单词就行了,而这个“词”的长度就是单词的长度。
由于本人怕跑循环会 TLE,但又不会哈希,于是决定用 STL,不会 STL 中的 string 的 find 的用法的同学见这篇文章。
提交记录,完美 AC。
代码:
#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;
}