【luogu P1026统计单词个数】 题解

统计单词个数

题目描述

给出一个长度不超过200200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。

要求将此字母串分成k份(1<=k40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。

当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含thisis,选用this之后就不能包含th)。

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

要求输出最大的个数。

输入格式

每组的第一行有2个正整数(p,kp,k)

pp表示字串的行数,k表示分为k个部分。

接下来的pp行,每行均有20个字符。

再接下来有1个正整数s,表示字典中单词个数。(1s6)

接下来的s行,每行均有11个单词。

输出格式

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

输入输出样例

输入 #1
1 3
thisisabookyouareaoh
4
is
a
ok
sab
输出 #1
7

说明/提示

this/isabookyoua/reaoh

在做这道题之前,可以先了解以下几个的STL函数。

1.  s.substr(x,y)  在s中取出下标从x开始长度为y的字符串(第一个字母下标为0)。

2.  s.find(t)  在s中寻找字符串t,找到则返回0,找不到则返回一个极大值。

3. s.length() 返回字符串s的长度。 

此外,分析题意,可以明确的判定为是一道区间dp题。

我们记

sum[i][j]为在字符串中q区间[ i , j ]里出现的单词个数。

dp[i][j]为处理完前i个字母,分成j个区间,能得到的单词个数。

那么得到转移方程:

dp[i][j] = max(dp[i][j] , dp[t-1][j-1] + sum[t][i]), t∈(j,i)

因为划分为j-1个区间,最骚需要j-1个字母,所以t从j开始dp。

最困难的转移方程解决后,我们在来想想怎么初始化。

题目要求说一个字母只能是一个单词的开头。

那么我们考虑倒序。

首先,我们从右往左枚举区间右端点。

然后从在右往左枚举区间左端点,先继承上一个左端点的sum,在判断当前的这个左端点是否能作为某个单词的开头。

如果能就+1.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int dp[1000][100],sum[1000][1000];
int p,k,m,len;
string s="0",ch,a[1000];
inline bool check(int l,int r){//判断是否有一个单词以l开头。
    string x=s.substr(l,r-l+1);
    for(int i=1;i<=m;i++)
        if(x.find(a[i])==0) return true;
    return false;
}
int main()
{
    int i,j,t;
    scanf("%d%d",&p,&k);
    for(i=1;i<=p;i++){
        cin>>ch;
        s+=ch;
    }
    scanf("%d",&m);
    for(i=1;i<=m;i++) cin>>a[i];
    len=s.length()-1;
    for(i=len;i>=1;i--)
    for(j=i;j>=1;j--){
        sum[j][i]=sum[j+1][i];//继承前一个区间
        if(check(j,i)) 
        sum[j][i]++;
    }
    for(i=1;i<=len;i++)
        for(j=1;j<=k&&j<=i;j++)
            for(t=j;t<=i;t++)//dp核心
                dp[i][j]=max(dp[i][j],dp[t-1][j-1]+sum[t][i]);
    printf("%d",dp[len][k]);
}

猜你喜欢

转载自www.cnblogs.com/quitter/p/11720772.html