如果一个由
'0'
和'1'
组成的字符串,是以一些'0'
(可能没有'0'
)后面跟着一些'1'
(也可能没有'1'
)的形式组成的,那么该字符串是单调递增的。我们给出一个由字符
'0'
和'1'
组成的字符串S
,我们可以将任何'0'
翻转为'1'
或者将'1'
翻转为'0'
。返回使
S
单调递增的最小翻转次数。
示例 1:
输入:"00110" 输出:1 解释:我们翻转最后一位得到 00111.
思路:
1、首先从首部开始寻找最先出现的‘1’位置l,从尾部寻找最后出现的‘0’的位置h
2、 对字符串的翻转出现在[l,h]区间中
3、以gap为分割点从l到h每次+1递增,判断gap左边‘1’的个数,判断gap右边‘0’的个数,相加求和,比较每个gap分割点的和的最小值,如果不做优化,会超时
4、建立rdp[i]保存以i为分割点左边的为'1'的个数,ldp[i]保存以i为分割点右边的'0'的个数,那么求rdp[gap]判断上一个点为'1',那么rdp[gap]=rdp[gap-1]+1,求ldp[gap]判断上一个节点为'0',那么ldp[gap] =ldp[gap-1]-1
class Solution {
public int[] rdp;
public int[] ldp;
public int minFlipsMonoIncr(String S) {
char[] nums = S.toCharArray();
if(nums.length==0){
return 0;
}
int l=-1;int h=-1;
int cmin=Integer.MAX_VALUE;
rdp = new int[nums.length+1];
ldp = new int[nums.length+1];
for(int i=0;i<nums.length;i++){
if(nums[i]=='1'){
l=i;
break;
}
}
//System.out.println(l);
for(int i=nums.length-1;i>=0;i--){
if(nums[i]=='0'){
h=i;
break;
}
}
if(l==-1||h==-1||l>=h){
return 0;
}
//System.out.println(h);
for(int i=l;i<=h+1;i++){
//gap需要一直判断到h+1,保证左边的元素全部判断
cmin = Math.min(cmin,countMin(l,h,i,nums));
}
return cmin;
}
public int countMin(int l,int h,int gap,char[] nums){
int count=0;
if(gap-1>=l&&rdp[gap-1]!=0){
if(nums[gap-1]=='1'){
rdp[gap]=rdp[gap-1]+1;
}else{
rdp[gap]=rdp[gap-1];
}
}else{
for(int i=l;i<gap;i++){
if(nums[i]=='1'){
count++;
}
}
rdp[gap]=count;
}
count=0;
if(gap-1>=l&&ldp[gap-1]!=0){
if(nums[gap-1]=='0'){
ldp[gap]=ldp[gap-1]-1;
}else{
ldp[gap]=ldp[gap-1];
}
}else{
for(int i=gap;i<=h;i++){
if(nums[i]=='0'){
count++;
}
}
ldp[gap]=count;
}
//System.out.println(rdp[gap]+" "+ldp[gap]);
return rdp[gap]+ldp[gap];
}
}