/**
* 字符串只包含0和1,将字符串翻转到单调递增
*/
public class Test {
/**
* 两种方法计算的值是一致的
* @param args
*/
public static void main(String[] args) {
String str = "000000110011111000111110";
System.out.println( getMinFlip(str) == minFlipsMonoIncrb(str));
}
/**
* 这种方法比较直接,将字符串分为两部分,第一部分统计1的个数,第二部分统计0的个数,再相加
* 时间复杂度为O(N^2)
* @param S
* @return
*/
public static int minFlipsMonoIncrb(String S) {
int result = S.length();
for (int i = 0; i < S.length(); i++) {
char[] str1 = S.substring(0, i).toCharArray();
char[] str2 = S.substring(i + 1, S.length()).toCharArray();
int zero = 0;
int one = 0;
for (int j = 0; j < str1.length; j++) {
if (str1[j] == '1')
zero++;
}
for (int j = 0; j < str2.length; j++) {
if (str2[j] == '0')
one++;
}
int re = zero + one;
if (re < result)
result = re;
}
return result;
}
/**
* 这种方法在计算int[] one 和int[] zero数组时候,利用了前一个下标的值来计算后一个的值
* 时间复杂度为O(N)
* @param str
* @return
*/
public static int getMinFlip(String str){
int len = str.length();
int [] one = new int[len]; // [i] = a 从左边开始统计,下标为i时,左边的1的个数为a
int[] zero = new int[len]; // [i] = b 从右边开始统计,下标为i时,右边的0的个数为b
if(str.charAt(0) == '1'){
one[0] = 1;
}else {
one[0] = 0;
}
for (int i = 1; i<len; i++){
if(str.charAt(i) == '1'){
one[i] = one[i-1]+1;
}else {
one[i] = one[i - 1];
}
}
if(str.charAt(len-1) == '0'){
zero[len-1] = 1;
}else{
zero[len-1] = 0;
}
for(int j = len-2;j>=0;j--){
if(str.charAt(j) == '0'){
zero[j] = zero[j + 1] + 1;
}else{
zero[j] = zero[j+1];
}
}
//one[len-1] 表示的意思是当把字符串变为全0时,要翻转的步数
//zero[0] 表示的意思是当把字符串变为全1时,要翻转的步数
int min = Integer.min(zero[0], one[len - 1]);
for(int k = 0; k < len-1; k++){
int temp = zero[k+1] + one[k] ;
//one[k] + zero[k+1] 组成一个分割点
min = Integer.min(temp, min);
}
return min;
}
}