P1026 [NOIP2001 提高组] 统计单词个数

本文介绍了一道NOIP2001提高组的算法题——统计单词个数的问题解决方法。文章详细阐述了如何通过动态规划来最大化分割字符串以获得最多的单词数,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[NOIP2001 提高组] 统计单词个数

题目描述

给出一个长度不超过 200200200 的由小写英文字母组成的字母串(该字串以每行 202020 个字母的方式输入,且保证每行一定为 202020 个)。要求将此字母串分成
kkk 份,且每份中包含的单词个数加起来总数最大。

每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this 中可包含 thisis,选用 this 之后就不能包含
th

单词在给出的一个不超过 666 个单词的字典中。

要求输出最大的个数。

输入格式

每组的第一行有两个正整数 p,kp,kp,k
ppp 表示字串的行数,kkk 表示分为 kkk 个部分。

接下来的 ppp 行,每行均有 202020 个字符。

再接下来有一个正整数 sss,表示字典中单词个数。
接下来的 sss 行,每行均有一个单词。

输出格式

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

样例 #1

样例输入 #1

1 3
thisisabookyouareaoh
4
is
a
ok
sab

样例输出 #1

7

提示

【数据范围】
对于 100%100\%100% 的数据,2≤k≤402 \le k \le 402k401≤s≤61 \le s \le 61s6

【样例解释】
划分方案为 this / isabookyoua / reaoh

【题目来源】
NOIP 2001 提高组第三题


Solucioˊn del problema\mathrm{Solución\ del\ problema}Solucioˊn del problema

由题可知,每个字母只能用作一个单词的开头,故可以先统计出每个区间的单词数:

g[i][j]g[i][j]g[i][j] 表示区间 [i,j][i,j][i,j] 的单词数量,则:

g[i][j]={g[i+1][j]        (以i开头无法构成单词)g[i+1][j]+1        (以i开头可以构成单词)g[i][j]=\begin{cases}g[i+1][j]\ \ \ \ \ \ \ \ (以i开头无法构成单词)\\\\g[i+1][j]+1\ \ \ \ \ \ \ \ (以i开头可以构成单词)\end{cases}g[i][j]=g[i+1][j]        (i)g[i+1][j]+1        (i)

接下来就可以统计答案:

f[i][j]f[i][j]f[i][j] 表示前 iii 个字母分成 jjj 段时,每段单词个数之和的最大值,则:

f[i][j]=max⁡j≤k<i{f[k][j−1]+g[k+1][i]}f[i][j]=\max\limits_{j\le k<i}\{f[k][j-1]+g[k+1][i]\}f[i][j]=jk<imax{f[k][j1]+g[k+1][i]}

kkk 个字母用 j−1j-1j1 次,后面 k+1∼ik+1\sim ik+1i111 次.

但注意到,这个式子在 j=1j=1j=1 时,f[i][1]f[i][1]f[i][1] 的值应为 g[1][i]g[1][i]g[1][i] ,但状态转移方程并不能枚举到这一项.

所以对 j=1j=1j=1 的情况做初始化. (改方程也行)


Coˊdigo\mathrm{Código}Coˊdigo

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int N=205;

char a[N],str[N],s[N][N];
int p,t,m,n;
int f[N][N],g[N][N],len[N];

bool check(int l,int r)
{
	for(register int i=1;i<=m;i++)
	{
		bool flag=0;
		if(r-l+1<len[i]) continue;
		for(register int j=l;j<=r&&j<=len[i]+l-1;j++)
			if(a[j]!=s[i][j-l+1]) {flag=1;break;}
		if(!flag) return true;
	}
	return false;
}

int main()
{
	scanf("%d%d",&p,&t);
	while(p--)
	{
		scanf("%s",str);
		strcat(a+1,str);
	}
	n=strlen(a+1);
	scanf("%d",&m);
	for(register int i=1;i<=m;i++)
		scanf("%s",s[i]+1),len[i]=strlen(s[i]+1);
	for(register int i=n;i>=1;i--)
		for(register int j=n;j>=i;j--)
			g[i][j]=g[i+1][j]+check(i,j);
	for(register int i=1;i<=n;i++) f[i][1]=g[1][i];
	for(register int i=1;i<=n;i++)
		for(register int j=2;j<=t&&j<=i;j++)
			for(register int k=j;k<i;k++)
				f[i][j]=max(f[i][j],f[k][j-1]+g[k+1][i]);
	printf("%d\n",f[n][t]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值