题目描述:
小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。 例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。 现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?
新手菜鸟,做这道题花了很多时间,参考了不少的思路,写下自己的理解,做个总结,希望对大家有帮助
import java.util.*;
/*
*动态规划类型
*
* 小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。
例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。
现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?
*/
public class StringReverse {
public static void stringRe(){
Scanner in = new Scanner(System.in);
String s = in.next();
char[] array = s.toCharArray();
int n = s.length();//01串长度
int[][][] dp = new int[n][n][2];
int sum = 0;//权值和
//从结尾开始遍历字符串
for (int i = n - 1; i >= 0; i--) {
//从当前字符往后直到结尾(取子串)
for (int j = i; j < n; j++) {
if (i == j) {
/*
总体思路:dp[j][0]和dp[j][1]分别表示将当前位修改为0和1时的权值和(注意此处说的是修改)。
所以:
1、当前位是1时,dp[j][0]中的[0]和dp[j][1]中的[1]也分别表示反转当前数字和不反转当前数字。
因为已经是1了,所以修改为0(dp[j][0]=1)表示反转,修改为1(dp[j][1])表示不反转,不用操
作
2、当前位是0时,dp[j][0]中的[0]和dp[j][1]中的[1]分别表示不反转当前数字和反转当前数字。因
为已经是0了,所以修改为0(dp[j][0])表示不反转,修改为1(dp[j][1]=1)表示反转
*/
if (array[i] == '0') {
dp[i][i][1] = 1;
} else {
dp[i][i][0] = 1;
}
} else {
int numFor0 = array[j] == '0' ? 0 : 1;
int numFor1 = array[j] == '0' ? 1 : 0;
/*状态转移:当前位既然修改为0了,要相邻位不相同,上一位必定要
修改为1,所以总的反转次数:递推成上一位取1时的权值
再加上当前数字的反转次数(numFor0)
*/
dp[i][j][0] = dp[i][j - 1][1] + numFor0;//当前数是0时的状态转移方程
/*状态转移:当前位既然修改为1了,要相邻位不相同,上一位必定要
修改为0,所以总的反转次数:递推成上一位取0时的权值
再加上当前数字的反转次数(numFor1)
*/
dp[i][j][1] = dp[i][j - 1][0] + numFor1;//当前数是1时的状态转移方程
}
sum += Math.min(dp[i][j][0], dp[i][j][1]);//反转次数取最小就是权值
}
}
System.out.println(sum);
}
}