洛谷P1026(统计单词个数)

洛谷P1026(统计单词个数)

题目描述

给出一个长度不超过 200 的由小写英文字母组成的字母串(该字串以每行 20 个字母的方式输入,且保证每行一定为 20 个)。要求将此字母串分成 k份,且每份中包含的单词个数加起来总数最大。
每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this 中可包含 this 和 is,选用 this 之后就不能包含 th。
单词在给出的一个不超过 6 个单词的字典中。
要求输出最大的个数。

输入格式

每组的第一行有两个正整数 p,k。 p 表示字串的行数,k 表示分为 k 个部分。
接下来的 p 行,每行均有 20 个字符。
再接下来有一个正整数 s,表示字典中单词个数。 接下来的 s 行,每行均有一个单词。

输出格式

1个整数,分别对应每组测试数据的相应结果。

解题思路

  这题用两个动态规划就可以解决了,我还多用了一个滚动数组。
  第一步是输入,这个是必须要做的,没啥说的,输入p,k。根据p按行输入一个字符串str,然后 int len=str.size()一下,这个后面要用的,之后输入s,再输入一个word数组。
  之后是预处理,用w[i][j]表示str从i到j中单词个数。先构造出w[i][i],之后递推式:w[i-1][j]=w[i][j],如果str中从i-1到j的子串中存在一个单词从i-1位置开始,那么w[i-1][j]再加一。这里可以用string类的find函数,返回值是0的话就加一。用f[i][j]表示把前i个字符分成j段最多的单词个数,要先处理f[i][i], f [ i ] [ i ] = ∑ k = 1 i w [ k ] [ k ] f[i][i]=\sum_{k=1}^{i}w[k][k] f[i][i]=k=1iw[k][k],之后就是一个递推式f[i][j]=max(f[i][j],f[r][j-1]+w[r+1][i]),这里的r满足 ( j − 1 ) ≤ r ≤ ( i − 1 ) (j-1)\le r\le (i-1) (j1)r(i1)
  最后一步就是输出,输出f[len][k]就好了。
  为什么要用到滚动数组呢,请看f数组部分,第k列只与第k-1列有关,同理,第k-1列只与第k-2列有关···那么我们就可以建立一个滚动数组,同时有个小技巧,f数组的第k列我们只取了f[len][k],其它都是没用的,所以我们滚动数组处理的时候只要处理到k-1步就可以了,第k简化成只求一个数了。
Talking is cheap,show you my code.

代码

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string str = "";
int p, k, s;
string word[6];
int w[202][202];
int f[202][2];
int check(string st) {
	for (int i = 0;i < s;i++) {
		if (st.find(word[i]) == 0) {
			return 1;
		}
	}
	return 0;
}
int main() {
	cin >> p >> k;
	for (int i = 0;i < p;i++) {
		string st;
		cin >> st;
		str = str + st;
	}
	cin >> s;
	for (int i = 0;i < s;i++) {
		cin >> word[i];
	}
	int len = str.size();
	for (int i = 1;i <= len;i++) {
		for (int j = i;j > 0;j--) {
			w[j][i] = w[j + 1][i];
			bool flag = false;
			string st = str.substr(j - 1, i - j + 1);
			w[j][i] += check(st);
		}
	}
	bool b = 0;
	for (int i = 1;i <= len;i++) {
		f[i][b] = w[1][i];
	}
	for (int cs = 2;cs < k;cs++) {
		for (int i = cs;i <= len;i++) {
			f[i][!b] = 0;
			for (int j = cs - 1;j < i;j++) {
				f[i][!b] = max(f[i][!b], f[j][b] + w[j + 1][i]);
			}
		}
		b = !b;
	}
	int ans = 0;
	for (int i = (k - 1);i < len;i++) {
		ans = max(ans, f[i][b] + w[i + 1][len]);
	}
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值