【题解】P1026 统计单词个数
题面
前言
数据范围极小,故此存在若干种《奇怪》的方法可以水过
正文
记 \(dp_{i,j}\) 表示前 \(i\) 个字母分成了 \(j\) 段时最大的单词数目
显然有枚举断点 \(l\),在这个位置将字符串分成两段,转移方程形如:
\begin{equation}
dp_{i,j} = \max \lbrace dp_{l,j-1}+s_{l+1,i} \rbrace
\nonumber
\end{equation}
其中 \(s_{i,j}\) 表示区间 \([i,j]\) 中,总共的单词数量
那么 \(s\) 数组可以用 \(O(|T|^3 \times s)\) 的效率预处理,然后 \(dp\) 数组可以用 \(O(|T|^2 \times k)\) 的处理
当然,其实到这里就差不多了,不过云落还是想追求一些优秀的时间复杂度
比如说我们可以给 \(s\) 套一个字典树,把常数 \(s\) 优化掉
再比如我们 \(s\) 的处理也可以通过递推的手段得到,没有必要做大量的枚举
还比如,\(dp\) 的过程可以通过单调队列优化把复杂度再降一维
理论时间复杂度 \(O(|T|^2+|T| \times k)\)
得亏这是个黄题
建议把 \(|T|\) 改大并且调整 \(k\) 的大小
代码
代码就放无优化的朴素 DP 吧,优化自己写捏
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int maxl=205,maxk=45,maxs=8;
int p,k,n;
string s,a[maxs];
int dp[maxl][maxk],sum[maxl][maxl];
inline 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;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>p>>k;
while(p--){
string str;
cin>>str;
s+=str;
}
int len=s.length();
s=' '+s;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=len;i>=1;i--){
for(int j=i;j>=1;j--){
sum[j][i]=sum[j+1][i];
if(check(j,i)){
sum[j][i]++;
}
}
}
dp[0][0]=0;
for(int i=1;i<=k;i++){
dp[i][i]=dp[i-1][i-1]+sum[i][i];
}
for(int i=1;i<=len;i++){
dp[i][1]=sum[1][i];
}
for(int i=1;i<=len;i++){
for(int j=1;j<=k&&j<i;j++){
for(int l=j;l<i;l++){
dp[i][j]=max(dp[i][j],dp[l][j-1]+sum[l+1][i]);
}
}
}
cout<<dp[len][k]<<endl;
return 0;
}
后记
洗洗睡
完结撒花!