其他分享
首页 > 其他分享> > 1057 [NOIP2001]统计单词个数 划分区间 线性DP

1057 [NOIP2001]统计单词个数 划分区间 线性DP

作者:互联网

链接:https://ac.nowcoder.com/acm/problem/16696
来源:牛客网

题目描述

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

输入描述:

每组的第一行有2个正整数(p,k)
p表示字串的行数,k表示分为k个部分。
接下来的p行,每行均有20个字符。
再接下来有1个正整数s,表示字典中单词个数。(1 ≤ s ≤ 6 )
接下来的s行,每行均有1个单词。

输出描述:

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

输入

复制
1 3
thisisabookyouareaoh
4
is
a
ok
sab

输出

复制
7

分析

这题问的是给一场串字母分块,问每块所拥有的单词数之和最大是多少。

所以得先预处理出来每一块区间内的字符串数量是多少,

DP 考虑一个dp[i][j]表示前i个字母分成了j段时最大的单词数目,那么我们枚举一个位置p,

在这个位置将字符串分成两段,那么这个dp[i][j]=max(dp[p][j-1]+s[p+1][i]),

我们定义s[i][j]s[i][j]表示在文本串(即文章)的第i个位置到第j个位置,以每个位置开头,以j为结尾,总共有多少单词。

那么s数组可以用lenght(T) ^ 3×number(单词)的效率预处理,然后dp数组可以用length(T) ^ 2×k的效率直接搞出来。

DP的优化手段:如果单词数目比较多弄个Trie就可以把number的常数直接弄掉。同时s数组应该还可以用递推搞一搞,那么又可以把预处理的效率弄成length(T)

2
。然后在实际操作中dp数组还可以用单调队列优化一下,那么就可以把一个length(T)给弄掉。

 

PS:发现ms(f,0x3f)是负无穷

#include<bits/stdc++.h>
using namespace std;
const int N = 210;
int val[N*N],ch[N*N][26],f[N][60],s[N][N];
int NodeCnt = 0,n,m,len,k;//字母个数,行数,单词个数,字母长度,分块数量。
string in,T;
void insert(string in) {
    int u = 0;
    for(int i = 0;i<in.length();i++ ) {
        int c = in[i] - 'a';
        if(!ch[u][c]) ch[u][c] = ++ NodeCnt;
        u = ch[u][c];
    }
    val[u] = 1;
}

int query(int l,int r) {
    int sum = 0;
    for(int i = l;i<=r;i++) {
        int u = 0;
        for(int j = i;j<=r;j++) {
            int c = T[j] - 'a';
            if(!ch[u][c]) break;
            u = ch[u][c];
            if(val[u]) {sum ++ ;break;}
        }
    }
    return sum;
}

int main() {
    ios::sync_with_stdio(0);
    while(cin>>n>>k) {
        T = "";
        for(int i = 0;i<n;i++ ) {
            cin>>in;
            T = T + in;
        }
        len = T.length();
        cin>>m;
        for(int i = 0;i<m;i++ ){
            cin>>in;
            insert(in);
        }
        for(int i = 0;i<len;i++ ){
            for(int j = i;j<len;j++ ) {
                s[i][j] = query(i,j);
            }
        }
        memset(f,~0x3f,sizeof f);
        for(int i = 0;i<len;i++) {
            f[i][1] = s[0][i];
        }
        for(int i = 0;i<len;i++) {
            for(int j = 2;j<=k;j++) {
                for(int p = 0;p<i;p++) {
                    f[i][j] = max(f[i][j],f[p][j-1] + s[p+1][i]);
                }
            }
        }
        cout<<f[len - 1][k];
    }
    return 0;
}

 

标签:NOIP2001,1057,int,字母,个数,单词,length,dp,DP
来源: https://www.cnblogs.com/er007/p/16508003.html