P1026 [NOIP 2001 提高组] 统计单词个数 题解

题意简析

题目要求将一个长度不超过 \(200\) 的字符串分割成 \(k\) 段,使得每段中包含的单词数量之和最大。单词来自一个给定的字典(不超过 \(6\) 个单词)。匹配单词时允许部分重叠,但每个单词的首字母只能使用一次(即一个字母不能作为多个单词的首字母)。

思路解析

显然是 DP。

对于字符串的每个位置 \(i\),计算以 \(i\) 为首字母的字典单词的最小结束位置(即 \(i + len - 1\) 的最小值,\(len\) 为单词长度)。若不存在匹配的单词,则标记为无穷大。

预处理一个二维数组 \(cnt[L][R]\),表示在区间 \([L, R]\) 内,满足最小结束位置不超过 \(R\) 的位置数(即该区间内可匹配的单词数)。

定义 \(dp[i][j]\) 表示将前 \(i\) 个字符分割成 \(j\) 段的最大单词数。状态转移方程为:

  • \(j = 1\) 时,做整个区间:\(dp[i][j] = cnt[0][i-1]\)

  • \(j > 1\) 时,枚举最后一段的起始位置:\(dp[i][j] = \max(dp[t][j-1] + cnt[t][i-1])\),其中 \(t\)\(j-1\)\(i-1\)

注意到数据范围极小,时间复杂度为 \(O(n^2k)\) 也可通过此题。

代码实现

#pragma G++ optimize("O3", "unroll-loops", "omit-frame-pointer", "inline")
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 210;   // 最大字符串长度
const int MAXK = 45;    // 最大分割段数
const int MAXDICT = 7;  // 最大字典单词数
const int INF = 300;    // 超过最大长度200即可
char s[MAXN], dict[MAXDICT][MAXN];
int p, k, n, d, minEnd[MAXN], cnt[MAXN][MAXN], dp[MAXN][MAXK];
int main() {
    cin >> p >> k;
    for (int i = 0; i < p; i++) {
        char line[25];
        cin >> line;
        strcat(s, line);  // 将每行拼接到s后面
    }
    n = strlen(s);  // 字符串总长度

    cin >> d;
    for (int i = 0; i < d; i++) {
        cin >> dict[i];
    }

    // 预处理minEnd数组:记录每个位置的最小结束位置
    for (int i = 0; i < n; i++) {
        minEnd[i] = INF;  // 初始化为INF
        for (int j = 0; j < d; j++) {
            int len = strlen(dict[j]);
            if (i + len > n)
                continue;  // 超出字符串范围

            bool match = true;
            for (int idx = 0; idx < len; idx++) {
                if (s[i + idx] != dict[j][idx]) {
                    match = false;
                    break;
                }
            }
            if (match) {
                minEnd[i] = min(minEnd[i], i + len - 1);
            }
        }
    }

    // 预处理cnt数组:区间[L,R]内可匹配的单词数
    for (int L = 0; L < n; L++) {
        for (int R = L; R < n; R++) {
            int sum = 0;
            for (int i = L; i <= R; i++) {
                if (minEnd[i] <= R) {
                    sum++;
                }
            }
            cnt[L][R] = sum;
        }
    }
    // 初始化dp数组为极小值
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= k; j++) {
            dp[i][j] = -10000;
        }
    }
    dp[0][0] = 0;  // 初始状态

    // 动态规划过程
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= min(i, k); j++) {
            if (j == 1) {
                dp[i][j] = cnt[0][i - 1];
            } else {
                for (int t = j - 1; t < i; t++) {
                    if (dp[t][j - 1] >= 0) {  // 有效状态
                        dp[i][j] = max(dp[i][j], dp[t][j - 1] + cnt[t][i - 1]);
                    }
                }
            }
        }
    }

    cout << dp[n][k] << endl;
    return 0;
}
posted @ 2025-07-31 22:37  TangyixiaoQAQ  阅读(9)  评论(0)    收藏  举报