【DP】开一个int[n+1][2]的dp数组其中n是字符串长度,2是指0和1,dp[i][0]表示到达第i个,并且保证i位置为0的情况下需要的最小的翻转数,dp[i][1]表示到达第i个,并且保证第i个是1的况下的最小翻转数,于是转移公式就成了:
(1)当第i个为0的情况下:
dp[i + 1][0] = dp[i][0]. 只能从0转移
dp[i + 1][1] = min(dp[i][0], dp[i][1]) + 1. 可以从0和1转移,因为要让当前变成1,所以还要+1
(2)当第i个为1的情况下:
dp[i + 1][0] = dp[i][0] + 1. 只能从0转移,并且要把当前的1翻转成0
dp[i + 1][1] = min(dp[i][0], dp[i][1]) 从0和1转移都行,并且不用变第i个字符
class Solution {
// DP 7:39
public int minFlipsMonoIncr(String s) {
int n = s.length();
int[][] dp = new int[n + 1][2];
for(var i = 1; i <= n; i++){
char c = s.charAt(i - 1);
if(c == '0'){
dp[i][0] = dp[i - 1][0];
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][1]) + 1;
}else{
dp[i][0] = dp[i - 1][0] + 1;
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][1]);
}
}
return Math.min(dp[n][0], dp[n][1]);
}
}
【前缀和】用前缀和来统计到达i时1的个数,字符串长度为n,1的总个数为m,那么0的个数就是n-m个,然后假设以i处划分i之前都是0,i之后是1。那么前面的1的个数都要变为0,后面的0的个数为(n - i) - (m - pre[i]),这些0要全部变为1,这样遍历一遍取最小值即可。
class Solution {
public int minFlipsMonoIncr(String s) {
// 前缀和 7:55 10
int n = s.length(), i;
int[] pre = new int[n + 1];
for(i = 1; i <= n; i++){
if(s.charAt(i - 1) == '1') pre[i] = pre[i - 1] + 1;
else pre[i] = pre[i - 1];
}
int ans = n;
for(i = 0; i <= n; i++){
int t = pre[i] + (n - i - pre[n] + pre[i]);
ans = Math.min(t, ans);
}
return ans;
}
}