• HDU-6549 String (前后缀优化dp)


    题目链接:HDU-6549 String

    题意

    wls 有一个长度为 $n$ 的字符串,每次他可以将一个长度不大于 $L$ 的子串修改成同一种字母,问至少修改多少次可以使字符串最多含有 $k$ 段。连续的只含同一种字母的子串被称为一段,比如说 aaabbccaaa 共含有 4 段。

    ($1leq n, L leq 10^5, 1leq k leq 10$)


    思路

    定义 dp[i][j][p],表示第 i 位为 j+'a' 时 [0, i] 至多 p 段的最小修改次数。

    如果 str[i] == j+'a',第 i 位不用修改,那么dp值直接从 i-1 转移过来:
    $$
    dp[i][j][p] = min(dp[i-1][j][p], dp[i-1][c][p-1]),(cin [0,25],c ot= j)
    $$

    否则第 i 位必须要修改一次,因为最后要求的是最少修改多少次使得字符串最多含有 k 段,根据贪心策略,尽量修改更多,使前面更多位与第 i 位为同一段,那么dp值从 max(i-L, 0) 转移过来:
    $$
    dp[i][j][p] = min(dp[q][j][p], dp[q][c][p-1])+1,(q = max(0, i-L),cin [0,25], c ot= j)
    $$

    这样时间复杂度是 $O(26^2nk)$,可以通过前后缀优化,优化掉 $c$ 的遍历。定义 $pre[i][j][p]$ 表示 $dp[i][0sim j][p]$ 的最小值,$suf[i][j][p]$ 表示 $dp[i][jsim 25][p]$ 的最小值,则

    当 str[i] == j+'a' 时:
    $$
    dp[i][j][p] = min(dp[i-1][j][p], pre[i-1][j-1][p-1], suf[i-1][j+1][p-1])
    $$
    当 str[i] != j+'a' 时:
    $$
    dp[i][j][p] = min(dp[q][j][p], pre[q][j-1][p-1], suf[q][j+1][p-1])+1,(q=max(0,i-L))
    $$

    这样时间复杂度是 $O(26nk)$。

    边界的转移要注意一下。


    代码实现 

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using std::max;
    using std::min;
    const int INF = 0x3f3f3f3f;
    const int N = 100010;
    // dp[i][j][p]表示第i位为j+'a'时[0,i]至多q段的最小修改次数
    // pre[i][j][p]表示dp[i][0~j][p]的最小值
    // suf[i][j][p]表示dp[i][j~25][p]的最小值
    int dp[N][27][11], pre[N][27][11], suf[N][27][11];
    char str[N];
    void init() {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < 26; j++) {
                dp[i][j][0] = INF;
                pre[i][j][0] = INF;
                suf[i][j][0] = INF;
            }
        }
        for (int i = 0; i < 26; i++) {
            if (str[0] == i + 'a') dp[0][i][1] = 0;
            else dp[0][i][1] = 1;
            if (i) pre[0][i][1] = min(pre[0][i-1][1], dp[0][i][1]);
            else pre[0][i][1] = dp[0][i][1];
        }
        suf[0][25][1] = dp[0][25][1];
        for (int i = 24; i >= 0; i--) suf[0][i][1] = min(suf[0][i+1][1], dp[0][i][1]);
    }
    
    int main() {
        int n, l, k;
        while (~scanf("%d %d %d %s", &n, &l, &k, str)) {
            init();
            for (int i = 1; i < n; i++) {
                for (int j = 0; j < 26; j++) {
                    for (int p = 1; p <= k; p++) {
                        if (str[i] == 'a' + j) {
                            dp[i][j][p] = min(dp[i-1][j][p], min((j ? pre[i-1][j-1][p-1] : INF), (j == 25 ? INF : suf[i-1][j+1][p-1])));
                        }
                        else {
                            int q = max(0, i - l);
                            dp[i][j][p] = min(dp[q][j][p], min((j ? pre[q][j-1][p-1] : INF), (j == 25 ? INF : suf[q][j+1][p-1]))) + 1;
                        }
                        if (j) pre[i][j][p] = min(pre[i][j-1][p], dp[i][j][p]);
                        else pre[i][j][p] = dp[i][j][p];
                    }
                }
                for (int j = 25; j >= 0; j--) {
                    for (int p = 1; p <= k; p++) {
                        if (j < 25) suf[i][j][p] = min(suf[i][j+1][p], dp[i][j][p]);
                        else suf[i][j][p] = dp[i][j][p];
                    }
                }
            }
            printf("%d
    ", pre[n-1][25][k]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    十二招让你的电脑桌变得更舒适
    【看后请推荐】程序员接私单不传秘籍之一二合编:加料更新!一定要看!
    【看后请推荐】程序员接私单不传秘籍之二:单子从哪儿来?
    【看后请推荐】程序员接私单不传秘籍之一:准备工作
    【看完请推荐】记国庆前的一次码农受骗记
    优化MySQL,还是使用缓存?读一篇文章有感
    大数据下Limit使用(MySQL)
    类Unix平台程序调试
    STL学习笔记
    MFC学习笔记
  • 原文地址:https://www.cnblogs.com/kangkang-/p/11642022.html
Copyright © 2020-2023  润新知