【题解】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;
}

后记

洗洗睡

完结撒花!

posted @ 2024-12-29 15:36  sunxuhetai  阅读(38)  评论(0)    收藏  举报