题目描述
小扣出去秋游,途中收集了一些红叶和黄叶,他利用这些叶子初步整理了一份秋叶收藏集 leaves
, 字符串 leaves
仅包含小写字符 r
和y
, 其中字符r
表示一片红叶,字符y
表示一片黄叶。
出于美观整齐的考虑,小扣想要将收藏集中树叶的排列调整成「红、黄、红」三部分。每部分树叶数量可以不相等,但均需大于等于 1。每次调整操作,小扣可以将一片红叶替换成黄叶或者将一片黄叶替换成红叶。请问小扣最少需要多少次调整操作才能将秋叶收藏集调整完毕。
样例
示例一
输入:leaves = "rrryyyrryyyrr"
输出:2
解释:调整两次,将中间的两片红叶替换成黄叶,得到 "rrryyyyyyyyrr"
示例二
输入:leaves = "ryr"
输出:0
解释:已符合要求,不需要额外操作
算法1
动态规划 $O(n)$
定义三个状态:
-
1
dp[i][1]
到i为止把树叶集变成全红的最小操作数 -
2.
dp[i][2]
到i为止把树叶集变成红黄的最小操作数 -
3.
dp[i][3]
到i为止把树叶集变成红黄红的最小操作数
状态转移:
- 1.
dp[i][1]
- 1.如果当前为r,直接从dp[i-1][1] 转移即可
- 2.如果当前为y,需要将当前y变成r 即dp[i-1][1] + 1
- 2.
dp[i][2]
- 1.如果当前为y,可以选择dp[i-1][1] 和 dp[i-1][2] 中较小的一个,这两种方案均合法
- 2.如果当前为r,需要对当前r变成y,即min(dp[i-1][2], dp[i-1][1]) + 1;
- 3.
dp[i][3]
与 dp[i][2] 同理
时间复杂度
遍历了三遍数组,状态数是O(3*N)的,即O(N)
C++ 代码
/*
Dp
dp[i][1] 到i为止全红 的最小操作数
dp[i][2] 到i为止红黄 的最小操作数
dp[i][3] 到i为止红黄红 的最小操作数
*/
class Solution {
public:
vector<vector<int>> dp;
int minimumOperations(string leaves) {
int n = leaves.length();
dp = vector<vector<int>> (n+1,vector<int>(4,0));
if(leaves[0] == 'r') dp[0][1] = 0;
else dp[0][1] = 1;
for(int i = 1; i<n; i++){
if(leaves[i] == 'r') dp[i][1] = dp[i-1][1];
else dp[i][1] = dp[i-1][1] + 1;
}
if(leaves[1] == 'y') dp[0][2] = dp[0][1];
else dp[0][2] = dp[0][1] + 1;
for(int i = 1; i<n; i++){
if(leaves[i] == 'y') dp[i][2] = min(dp[i-1][2],dp[i-1][1]);
else dp[i][2] = min(dp[i-1][2],dp[i-1][1]) + 1;
}
if(leaves[2] == 'r') dp[0][3] = dp[0][2];
else dp[0][3] = dp[0][2] + 1;
for(int i = 1; i<n; i++){
if(leaves[i] == 'r') dp[i][3] = min(dp[i-1][3],dp[i-1][2]);
else dp[i][3] = min(dp[i-1][3],dp[i-1][2]) + 1;
}
return dp[n-1][3];
}
};