【codevs1040】【01NOIPTG】统计单词个数,字符串的划分DP

1040 统计单词个数 2001年NOIP全国联赛提高组
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold

题目描述 Description
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(k:1-40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)(管理员注:这里的不能再用指的是位置,不是字母本身。比如thisis可以算做包含2个is)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。

输入描述 Input Description
第一行为一个正整数(n<=5)表示有n组测试数据
每组的第一行有二个正整数(p,k)
p表示字串的行数;
k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
接下来的s行,每行均有一个单词。

输出描述 Output Description
每行一个整数,分别对应每组测试数据的相应结果。

样例输入 Sample Input
1
1 3
thisisabookyouareaoh
4
is
a
ok
sab

样例输出 Sample Output
7

数据范围及提示 Data Size & Hint
this/isabookyoua/reaoh
写在前面:这道题告诉我们,DP时循环控制变量范围一定要把握好,我刚开始40分,改一个+1得60,再改一个2变1得80,加一个判断条件就AC了╮(╯▽╰)╭
—————————————最熟悉的龙套——————————————
解题思路:跟其他划分性DP的想法基本一致,前i个字母分成j个部分可以取出l个字母单独作为一部分(好像说的很混乱,还是看方程吧)
f[i][j]=max(f[i][j],f[i-l][j-1]+{(i-l+1)到j中单词个数})

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
using namespace std;
int n,p,k,s,f[201][41],flag[201];//flag判断单词开头是否重复
string s1="",s2[7],s3;
int ff(int x,int y)
{
    x--;
    y--;//字符串起始下标为0,所以自减
    int ans=0;
    memset(flag,false,sizeof(flag));
    string s4=s1.substr(x,y-x+1);
    for (int i=1;i<=s;i++)
    {
        int m=s4.find(s2[i],0);
        while (m>=0&&m<=s4.length()&&!flag[m+x])//m+x指总字符串s1的下标
        {
            flag[m+x]=true;
            ans++;
            m=s4.find(s2[i],m+1);//继续搜索下一个同样的单词
        }
    }
    return ans;
}
main()
{
    scanf("%d",&n);
    for (int q=1;q<=n;q++)
    {
        scanf("%d%d",&p,&k);
        for (int i=1;i<=p;i++) 
        {
            cin>>s3;
            s1+=s3;
        }
        scanf("%d",&s);
        for (int i=1;i<=s;i++) cin>>s2[i];
        memset(f,0,sizeof(f));//因为要多次读入,所以要初始化
        for(int i=1;i<=k;i++)
        for (int j=i;j<=s1.length();j++)
        for (int l=1;l<=j;l++)
        if ((j-l)>=i-1)//若j-l<i-1,则这些字母(j-l)不可能分成i-1份
        f[j][i]=max(f[j-l][i-1]+ff(j-l+1,j),f[j][i]);
    }
    printf("%d",f[s1.length()][k]);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值