链接: 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;
}