[NOIP2001 提高组] 统计单词个数
题目描述
给出一个长度不超过 200200200 的由小写英文字母组成的字母串(该字串以每行 202020 个字母的方式输入,且保证每行一定为 202020 个)。要求将此字母串分成
kkk 份,且每份中包含的单词个数加起来总数最大。
每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串 this
中可包含 this
和 is
,选用 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 402≤k≤40,1≤s≤61 \le s \le 61≤s≤6。
【样例解释】
划分方案为 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]=maxj≤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]}
前 kkk 个字母用 j−1j-1j−1 次,后面 k+1∼ik+1\sim ik+1∼i 用 111 次.
但注意到,这个式子在 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;
}