[NOIP2001 提高组] 统计单词个数
题目描述
给出一个长度不超过
200
200
200 的由小写英文字母组成的字母串(该字串以每行
20
20
20 个字母的方式输入,且保证每行一定为
20
20
20 个)。要求将此字母串分成
k
k
k 份,且每份中包含的单词个数加起来总数最大。
每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this
中可包含 this
和 is
,选用 this
之后就不能包含
th
。
单词在给出的一个不超过 6 6 6 个单词的字典中。
要求输出最大的个数。
输入格式
每组的第一行有两个正整数
p
,
k
p,k
p,k。
p
p
p 表示字串的行数,
k
k
k 表示分为
k
k
k 个部分。
接下来的 p p p 行,每行均有 20 20 20 个字符。
再接下来有一个正整数
s
s
s,表示字典中单词个数。
接下来的
s
s
s 行,每行均有一个单词。
输出格式
1 1 1个整数,分别对应每组测试数据的相应结果。
样例 #1
样例输入 #1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
样例输出 #1
7
提示
【数据范围】
对于
100
%
100\%
100% 的数据,
2
≤
k
≤
40
2 \le k \le 40
2≤k≤40,
1
≤
s
≤
6
1 \le s \le 6
1≤s≤6。
【样例解释】
划分方案为 this / isabookyoua / reaoh
【题目来源】
NOIP 2001 提高组第三题
S o l u c i o ˊ n d e l p r o b l e m a \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] 表示前 i i i 个字母分成 j j j 段时,每段单词个数之和的最大值,则:
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]=j≤k<imax{f[k][j−1]+g[k+1][i]}
前 k k k 个字母用 j − 1 j-1 j−1 次,后面 k + 1 ∼ i k+1\sim i k+1∼i 用 1 1 1 次.
但注意到,这个式子在 j = 1 j=1 j=1 时, f [ i ] [ 1 ] f[i][1] f[i][1] 的值应为 g [ 1 ] [ i ] g[1][i] g[1][i] ,但状态转移方程并不能枚举到这一项.
所以对
j
=
1
j=1
j=1 的情况做初始化. (改方程也行)
C o ˊ d i g o \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;
}