HDU 6549 - String (dp 前缀优化)

链接: String

在这里插入图片描述
题意:
有一个字符串 ,每次可以将一个长度不大于 l 的子串修改成同一种字母,问至少修改多少次可以使字符串最多含有 k 段。

思路
dp[ i ][ j ][ k ] 表示 将前 i 个分成 j 段 当前字符 为 k 的最小操作次数 。状态转移就是 如果当前这一位不修改 ,可以由 前一位分成 j 段 字符也为 k 转移 ,或者 由前一位分为 j - 1段 ,这一位不为 k 转移 ,这里就会有 25 种情况 ,如果再加一次循环肯定会 T , 所以可以在 dp 过程用前缀最小值记录。当前这一位修改也是同理 ,可以修改 l 位 ,所以肯定优先从 i - l 转移。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int n,l,k,ans = 1e9;
int dp[maxn][12][30],m[maxn][30];
char s[maxn];
int main() {
    
    
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n>>l>>k;
    cin>>s + 1;
    memset(dp,0x3f3f3f3f,sizeof(dp));
    memset(m,0x3f3f3f3f,sizeof(m));
    for(int i = 0; i < 26; i ++){
    
    
        dp[0][0][i] = 0;
        if(s[i] - 'a' == i) dp[1][1][i] = 0;
        else dp[1][1][i] = 1;
    }
    m[0][0] = m[1][1] = 0;
    for(int i = 2; i <= n; i ++){
    
    
        for(int j = 1; j <= k; j ++){
    
    
            for(int h = 0; h < 26; h ++){
    
    
                if(s[i] - 'a' == h){
    
    
                    dp[i][j][h] = min(dp[i][j][h] , dp[i-1][j][h]);
                    dp[i][j][h] = min(dp[i][j][h] , m[i-1][j-1]);
                }
                else {
    
    
                    dp[i][j][h] = min(dp[i][j][h] , dp[max(0 , i - l)][j][h] + 1);
                    dp[i][j][h] = min(dp[i][j][h] , m[max(0 , i - l)][j-1] + 1);
                }
                m[i][j] = min(m[i][j] , dp[i][j][h]);
            }
        }
    }
   for(int i = 1; i <= k; i ++){
    
    
       for(int j = 0;  j < 26 ; j ++ ){
    
    
           ans = min(ans , dp[n][i][j]);
       }
   }
   printf ("%d\n",ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/hddddh/article/details/108991780