HDU - 6549 String (前缀优化dp)

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

Input

第一行三个整数 n,l,k。 
第二行一个字符串。 
1 ≤ n ≤ 100, 000 
1 ≤ l ≤ 100, 000 
1 ≤ k ≤ 10

Output

一行一个数表示答案。

Sample Input

3 1 1
bab

Sample Output

1

定义状态 dp[i][j][k] 表示第 i 位前分了 j 段,操作后当前字符为 k 的最小操作次数

(1)s[i] == k

当前位不需要修改,直接从第 i - 1 位转移过来,分为两种情况,s[i - 1] == k 和  s[i - 1] != k

dp[i][j][k] = min(dp[i - 1][j][k], dp[i - 1][j - 1][c]), c\in [0, 26)

(2)s[i] != k

当前位需要修改,由于一次最多可以修改L个字符,所以要优先从i - L处转移,同样分为两种情况,s[i - L] == k 和 s[i - L] != k

dp[i][j][k] = min(dp[i - L][j][k], dp[i - L][j - 1][c]), c\in [0, 26)

可以发现当前的复杂度为O(n*10*26*26),肯定会T,那么我们引入一个pre[i][j]表示第 i 位(含)分了 j 段的最小修改次数,每次更新完dp[i][j][k] 之后更新pre[i][j] 即可。

引入pre[i][j]后的转移方程:

(1)s[i] == k

dp[i][j][k] = min(dp[i - 1][j][k], pre[i - 1][j - 1])

(2)s[i] != k

dp[i][j][k] = min(dp[i - L][j][k],pre[i - L][j - 1])​​​​​​​

前缀优化dp ?就这?

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 7;

int dp[N][15][30], pre[N][15];

int main() {
    int n, p, l;
    string s;
    scanf("%d%d%d", &n, &l, &p);
    cin >> s;
    s = "#" + s;
    memset(dp, inf, sizeof(dp));
    memset(pre, inf, sizeof(pre));
    for(int i = 0; i < 26; ++i) {
        dp[0][0][i] = 0;
        dp[1][1][i] = 1;
        if(s[1] == i + 'a') dp[1][1][i] = 0;
    }
    pre[0][0] = 0;
    pre[1][1] = 0;
    for(int i = 2; i <= n; ++i) {
        for(int j = 1; j <= p; ++j) {
            for(int k = 0; k < 26; ++k) {
                if(s[i] == k + 'a')
                    dp[i][j][k] = min({dp[i][j][k], dp[i - 1][j][k], pre[i - 1][j - 1]});
                else {
                    int id = max(0, i - l);
                    dp[i][j][k] = min({dp[i][j][k], dp[id][j][k] + 1, pre[id][j - 1] + 1});
                }
                pre[i][j] = min(pre[i][j], dp[i][j][k]);
            }
        }
    }
    int ans = inf;
    for(int i = 1; i <= p; ++i)
        for(int j = 0; j < 26; ++j)
            ans = min(ans, dp[n][i][j]);
    printf("%d\n", ans);
	return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值