划分型动态规划之统计单词个数

题目描述  Description

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

输入描述  Input Description

第一行为一个正整数(0<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

解题思路:要解决该问题,首先需要找出在给出的字符串中所有的字串所包含的字典中的单词个数,我们可以用map[i][j]的二维数组记录,map[i][j]表示字符串i到j的单词个数。利用find函数找出map[i][j]的值,当然一个个的找也能找出来,但比较费时,可以先从字符串的末尾找起,一次向前移动,如果移动的字符串构成一个新单词就加一,否则就为原来的数目。代码如下:
void findwd()
{
    int i,j,m;
    bool flag;
    memset(map,0,sizeof(map));
    for(i=a.length()-1;i>=0;i--)
    {
        for(j=i;j>=0;j--)
        {
            for(m=0,flag=0;m<s;m++)
            {
                if(a.find(e[m],j)==j&&j+e[m].size()<=i+1)
                {
                    flag=1;
                    break;
                }
            }
            if(flag)
                map[j][i]=map[j+1][i]+1;
            else
                map[j][i]=map[j+1][i];
        }
    }

}

注:position=string.find(str,position1),position是str首字母出现位置,position1是从string的什么位置开始查找。

  找出了对应的map[i][j],在利用划分型动态规划,对字符串进行划分,找出最优解。

代码如下:

int solve()
{
    int i,j,m;
    for(i=0;i<a.size();i++)
        dp[i][1]=map[0][i];
    for(m=2;m<=k;m++)
    {
        for(i=m;i<a.size();i++)
        {
            for(j=i-1;j>m-1;j--)
                dp[i][m]=max(dp[i][m],dp[j][m-1]+map[j+1][i]);
        }
    }
    return dp[a.size()-1][k];
}

全部代码:

#include<iostream>
#include<string>
#include<cstring>

using namespace std;
string a,e[6];
const int maxn=205,maxnn=45;
int map[maxn][maxn],dp[maxn][maxnn];
int k,s,len[6];

void findwd()
{
    int i,j,m;
    bool flag;
    memset(map,0,sizeof(map));
    for(i=a.length()-1;i>=0;i--)
    {
        for(j=i;j>=0;j--)
        {
            for(m=0,flag=0;m<s;m++)
            {
                if(a.find(e[m],j)==j&&j+e[m].size()<=i+1)
                {
                    flag=1;
                    break;
                }
            }
            if(flag)
                map[j][i]=map[j+1][i]+1;
            else
                map[j][i]=map[j+1][i];
        }
    }

}
int solve()
{
    int i,j,m;
    for(i=0;i<a.size();i++)
        dp[i][1]=map[0][i];
    for(m=2;m<=k;m++)
    {
        for(i=m;i<a.size();i++)
        {
            for(j=i-1;j>m-1;j--)
                dp[i][m]=max(dp[i][m],dp[j][m-1]+map[j+1][i]);
        }
    }
    return dp[a.size()-1][k];
}
int main()
{
    int t,p;
    cin>>t;
    while(t--&&cin>>p>>k)
    {
        a.clear();
        string b;
        while(p--&&cin>>b)
            a+=b;
        cin>>s;
        for(int i=0;i<s;i++)
        {
            cin>>e[i];
            len[i]=e[i].length();
        }
        findwd();
        cout<<solve()<<endl;
    }
    return 0;
}

 dp[i][m]表示从零到i的m次划分的单词个数。

转载于:https://www.cnblogs.com/weifengxiyu/p/5421887.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值