CODEVS 1040统计单词个数

对于这种序列型的动态规划,常用的处理方案是利用W[l][r]表示从l到r的序列所能得到的最大目标函数值。
由于本题还有一个划分次数的限制,因此,容易想到的是把常用方案改写成
W[l][r][limit]这种形式。
但是显然这种形式的维护效率很低。实际上,经过仔细思考,要计算出最后的答案W[0][end][limit],并不需要求出所有的W值,而只需要求出limit为0的时候的全部W[l][r]和l为0的时候的全部W[r][limit]即可。

/*
1040 统计单词个数  2001年NOIP全国联赛提高组
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold
题解
题目描述 Description
给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)(管理员注:这里的不能再用指的是位置,不是字母本身。比如thisis可以算做包含2个is)。
单词在给出的一个不超过6个单词的字典中。
要求输出最大的个数。

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

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



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

1
2 3
thisisabookyouareaoh
thisisabookyouareaoh
4
is
a
ok
sab
样例输出 Sample Output
7
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
/*
通过DP获得以下两个数组
两个数组(以下的r都视为超尾指针)
1.W[l][r]       l到r这一段的最多能有多少个单词
计算算法 :
W[r][r] = 0;
W[l - 1][r] = W[l][r] + 1(如果存在text[l]开头的当初的话)

2.V[r][t]       从头部到r的这一段,经过t次划分,存在多少个单词
计算算法:
V[r][0] = W[0][r],V[0][t] = 0;
V[r][t] = MAX(...V[m][t - 1] + W[m][r]...)(t <= m < r)
*/

int W[250][250];
int V[250][50];
string text,buf;
vector<string>word;

void input(string & text,int p){
    //输入字符串 p行
    text.clear();
    word.clear();
    for (int i = 0; i < p; i++){
        cin >> buf;
        text += buf;
    }
    int Num;
    cin >> Num;
    for (int i = 0; i < Num; i++){
        cin >> buf;
        word.push_back(buf);
    }
}
bool check(int l,int r){
    //检查是否存在字符串
    int i,j;

    buf.resize(r - l);
    copy(text.begin() + l, text.begin() + r, buf.begin());
    for (i = 0; i < word.size(); i++){
        int len = word[i].size();
        if (len > buf.size())continue;

        for (j = 0; j < len; j++){
            if (word[i][j] != buf[j])break;
        }
        if (j == len)return true;

    }
    return false;
}
void CalW(int len){
    //计算W的值
    int l,r;

    for (r = len; r > 0; r--){
        W[r][r] = 0;
        for (l = r - 1; l >= 0; l--){
            W[l][r] = W[l + 1][r];
            if (check(l,r))W[l][r]++;
        }
    }
}
void CalV(int len,int Tmax){
    int r, t, i;
    memset(V, 0, sizeof(V));
    for (r = 1; r <= len; r++)V[r][0] = W[0][r];

    for (t = 1; t <= Tmax; t++){
        for  (r = 1; r <= len; r++){
            for (i = t; i < r; i++){
                V[r][t] = max(V[r][t], V[i][t - 1] + W[i][r]);
            }
        }
    }
}
int main(void){
    int i, j, T;
    cin >> T;
    for (int TT = 0; TT < T; TT++){
        cin >> i >> j;
        input(text,i);
        CalW(i * 20);
        CalV(i * 20,j - 1);//分成j个部分,说明被分了j - 1次
        cout << V[text.size()][j - 1] << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值