洛谷 P1026 统计单词个数(区间DP 暴力)

题目大意:

有一个单词串An,可以把单词串分成k份,

定义区间单词数如下:

已知有一个单词字典如:apple,hi.....

在串中的单词数:然后在串中统计有多少个单词,唯一注意的是,若串中某个字母作为单词首字母用了,那么这个串中的字母就不能作为下一个单词的首字母。

例如:thise

字典中有单词this 和 thise

那么这个串中只能有this或者thise,而不能两个单词都存在。

问:我们怎么对单词划分,可以使得每一份中的单词数相加后最多。

解题思路:

由于求最大值,而且是区间问题,考虑区间DP。另一种解题入手点是贪心,但是这里没办法证明贪心是有效的。

定义状态memo[n][k],表示长度为n的序列,划分k份的最大单词数。

我们可以得到状态转移如下:

for r = k-1:n

memo[n][k] = max( memo[r][k-1] + w[r+1][n])

其中w[i][j],表明区间[i,j]包含的单词数。

w[i][j]可以使用暴力求解。具体为枚举i,j的同时,在区间内走,同时枚举单词。

废话:

这种区间题,要想到区间DP。

同时,暴力解题的思路我们要为了降低复杂度,我们要考虑多次枚举。

#include <bits/stdc++.h>
using namespace std;
const int MAXN =210;
const int MAXK =50;
int memo[MAXN][MAXK];
int w[MAXN][MAXN];
int dfs(int n,int k){
	if(k==1)return memo[n][k]=w[1][n];
	if(n==k){
		int sum=0;
		for(int i=0;i<k;i++){
			sum+=w[i][i];
		}
		return memo[n][k]=sum;
	}
	if(memo[n][k]!=-1)return memo[n][k];
	int tmpans=-1;
	for(int r=n-1;r>=k-1;r--){
		tmpans=max(tmpans,dfs(r,k-1)+w[r+1][n]);
	}
	return memo[n][k]=tmpans;
}
int main(){
	int p,k;cin>>p>>k;
	string str("");
	for(int i=0;i<p;i++){
		string tmp;cin>>tmp;
		str+=tmp;
	}
	int word;cin>>word;
	map<int,vector<string>> mm;
	for(int i=0;i<word;i++){
		string wo;cin>>wo;
		mm[wo[0]-'a'].push_back(wo);
	}
	int sz=str.size();
	for(int i=0;i<sz;i++){
		for(int j=i;j<sz;j++){
			int sum=0;
			for(int z=i;z<=j;z++){
				int suc=0;
				for(auto it:mm[str[z]-'a']){
					int count=0;
					while(count<(int)it.size()&&z+count<=j&& str[z+count]==it[count])count++;
					if(count==(int)it.size()){
						suc=1;
						break;
					}
				}
				if(suc)sum++;
			}
			w[i+1][j+1]=sum;
		}
	}
	memset(memo,-1,sizeof(memo));
	int ans=dfs(sz,k);
	cout<<ans<<endl;
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值