洛谷 P1026 统计单词个数 区间DP

题目描述

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

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

要求输出最大的个数。

输入输出格式

输入格式:

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

p表示字串的行数;

k表示分为k个部分。

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

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

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

输出格式:

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

输入输出样例

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

方法挺直接的,先求出每一段字符串包含了多少单词,再求出将整个字符串分割成 m 段最多可以获得多少单词。

dp[i][j]  表示前 i 个字符组成的串,分割成 j 段,最多能获得多少单词。

dp[i][j] = max(dp[k][j-1] + wordNum[k+1][length])   (j-1 <= k <= length-1)

代码:

#include <iostream>
#include <cstring>
using namespace std;

const int MAX = 205;
const int INF = 0x3fffffff;

int wordNum[MAX][MAX];        //i 到 j 这一段包括了多少个单词
int dp[MAX][MAX];        //剩下的 i 串,分成 j 段最多包含多少单词 
string s;
string words[10];
int l, m, n;

int dfs(int i, int j);        //枚举 

int main(){
//    freopen("input.txt", "r", stdin);
    cin >> l >> m;
    s = "";
    while(l--){
        string t;
        cin >> t;
        s += t;
    }
    
    cin >> n;
    for(int i=1; i<=n; i++){
        cin >> words[i];
    }
    
    //求出 dp 数组
    memset(wordNum, 0, sizeof(wordNum));
    for(int len = 1; len <=s.length(); len++){
        for(int i=0; i+len-1<s.length(); i++){
            int j = i + len - 1;
            bool hasWord = false;
            for(int x=1; x<=n; x++){        //从最左边的字符开始能不能构成一个单词 
                string t = words[x];
                int p = i, q = 0;
                while(p <= j && q < words[x].length()){
                    if(s[p] != words[x][q])
                        break;
                    p++;  q++;
                }
                if(q >= words[x].length()){
                    hasWord = true;
                    break;
                }
            }
            wordNum[i][j] = wordNum[i+1][j] + hasWord;    //能够成就加一 
        }
    }
    
    memset(dp, 0, sizeof(dp));
    for(int i=0; i<s.length(); i++){
        dp[i][1] = wordNum[0][i];
    }
    for(int j=2; j<=m; j++){                    //分割成 j 段 
        for(int i=0; i<s.length(); i++){        //有 i 个字符 
            for(int k=j-2; k<i; k++){                //分割点
                dp[i][j] = max(dp[i][j], dp[k][j-1] + wordNum[k+1][i]);
            }
        }
    }
    
    cout << dp[s.length()-1][m];
    
    return 0;
}
原文地址:https://www.cnblogs.com/lighter-blog/p/7400392.html