|
|
|
|
package class46;
|
|
|
|
|
|
|
|
|
|
// 如果一个字符相邻的位置没有相同字符,那么这个位置的字符出现不能被消掉
|
|
|
|
|
// 比如:"ab",其中a和b都不能被消掉
|
|
|
|
|
// 如果一个字符相邻的位置有相同字符,就可以一起消掉
|
|
|
|
|
// 比如:"abbbc",中间一串的b是可以被消掉的,消除之后剩下"ac"
|
|
|
|
|
// 某些字符如果消掉了,剩下的字符认为重新靠在一起
|
|
|
|
|
// 给定一个字符串,你可以决定每一步消除的顺序,目标是请尽可能多的消掉字符,返回最少的剩余字符数量
|
|
|
|
|
// 比如:"aacca", 如果先消掉最左侧的"aa",那么将剩下"cca",然后把"cc"消掉,剩下的"a"将无法再消除,返回1
|
|
|
|
|
// 但是如果先消掉中间的"cc",那么将剩下"aaa",最后都消掉就一个字符也不剩了,返回0,这才是最优解。
|
|
|
|
|
// 再比如:"baaccabb",
|
|
|
|
|
// 如果先消除最左侧的两个a,剩下"bccabb",
|
|
|
|
|
// 如果再消除最左侧的两个c,剩下"babb",
|
|
|
|
|
// 最后消除最右侧的两个b,剩下"ba"无法再消除,返回2
|
|
|
|
|
// 而最优策略是:
|
|
|
|
|
// 如果先消除中间的两个c,剩下"baaabb",
|
|
|
|
|
// 如果再消除中间的三个a,剩下"bbb",
|
|
|
|
|
// 最后消除三个b,不留下任何字符,返回0,这才是最优解
|
|
|
|
|
public class Code03_DeleteAdjacentSameCharacter {
|
|
|
|
|
|
|
|
|
|
// 暴力解
|
|
|
|
|
public static int restMin1(String s) {
|
|
|
|
|
if (s == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (s.length() < 2) {
|
|
|
|
|
return s.length();
|
|
|
|
|
}
|
|
|
|
|
int minLen = s.length();
|
|
|
|
|
for (int L = 0; L < s.length(); L++) {
|
|
|
|
|
for (int R = L + 1; R < s.length(); R++) {
|
|
|
|
|
if (canDelete(s.substring(L, R + 1))) {
|
|
|
|
|
minLen = Math.min(minLen, restMin1(s.substring(0, L) + s.substring(R + 1, s.length())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return minLen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean canDelete(String s) {
|
|
|
|
|
char[] str = s.toCharArray();
|
|
|
|
|
for (int i = 1; i < str.length; i++) {
|
|
|
|
|
if (str[i - 1] != str[i]) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 优良尝试的暴力递归版本
|
|
|
|
|
public static int restMin2(String s) {
|
|
|
|
|
if (s == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (s.length() < 2) {
|
|
|
|
|
return s.length();
|
|
|
|
|
}
|
|
|
|
|
char[] str = s.toCharArray();
|
|
|
|
|
return process(str, 0, str.length - 1, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// str[L...R] 前面有没有跟着[L]字符,has T 有 F 无
|
|
|
|
|
// L,R,has
|
|
|
|
|
// 最少能剩多少字符,消不了
|
|
|
|
|
public static int process(char[] str, int L, int R, boolean has) {
|
|
|
|
|
if (L > R) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (L == R) {
|
|
|
|
|
return has ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
int index = L;
|
|
|
|
|
int K = has ? 1 : 0;
|
|
|
|
|
while (index <= R && str[index] == str[L]) {
|
|
|
|
|
K++;
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
// index表示,第一个不是[L]字符的位置
|
|
|
|
|
int way1 = (K > 1 ? 0 : 1) + process(str, index, R, false);
|
|
|
|
|
int way2 = Integer.MAX_VALUE;
|
|
|
|
|
for (int split = index; split <= R; split++) {
|
|
|
|
|
if (str[split] == str[L] && str[split] != str[split - 1]) {
|
|
|
|
|
if (process(str, index, split - 1, false) == 0) {
|
|
|
|
|
way2 = Math.min(way2, process(str, split, R, K != 0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Math.min(way1, way2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 优良尝试的动态规划版本
|
|
|
|
|
public static int restMin3(String s) {
|
|
|
|
|
if (s == null) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (s.length() < 2) {
|
|
|
|
|
return s.length();
|
|
|
|
|
}
|
|
|
|
|
char[] str = s.toCharArray();
|
|
|
|
|
int N = str.length;
|
|
|
|
|
int[][][] dp = new int[N][N][2];
|
|
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
|
|
for (int j = 0; j < N; j++) {
|
|
|
|
|
for (int k = 0; k < 2; k++) {
|
|
|
|
|
dp[i][j][k] = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dpProcess(str, 0, N - 1, false, dp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int dpProcess(char[] str, int L, int R, boolean has, int[][][] dp) {
|
|
|
|
|
if (L > R) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
int K = has ? 1 : 0;
|
|
|
|
|
if (dp[L][R][K] != -1) {
|
|
|
|
|
return dp[L][R][K];
|
|
|
|
|
}
|
|
|
|
|
int ans = 0;
|
|
|
|
|
if (L == R) {
|
|
|
|
|
ans = (K == 0 ? 1 : 0);
|
|
|
|
|
} else {
|
|
|
|
|
int index = L;
|
|
|
|
|
int all = K;
|
|
|
|
|
while (index <= R && str[index] == str[L]) {
|
|
|
|
|
all++;
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
int way1 = (all > 1 ? 0 : 1) + dpProcess(str, index, R, false, dp);
|
|
|
|
|
int way2 = Integer.MAX_VALUE;
|
|
|
|
|
for (int split = index; split <= R; split++) {
|
|
|
|
|
if (str[split] == str[L] && str[split] != str[split - 1]) {
|
|
|
|
|
if (dpProcess(str, index, split - 1, false, dp) == 0) {
|
|
|
|
|
way2 = Math.min(way2, dpProcess(str, split, R, all > 0, dp));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ans = Math.min(way1, way2);
|
|
|
|
|
}
|
|
|
|
|
dp[L][R][K] = ans;
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String randomString(int len, int variety) {
|
|
|
|
|
char[] str = new char[len];
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
|
str[i] = (char) ((int) (Math.random() * variety) + 'a');
|
|
|
|
|
}
|
|
|
|
|
return String.valueOf(str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
int maxLen = 16;
|
|
|
|
|
int variety = 3;
|
|
|
|
|
int testTime = 100000;
|
|
|
|
|
System.out.println("测试开始");
|
|
|
|
|
for (int i = 0; i < testTime; i++) {
|
|
|
|
|
int len = (int) (Math.random() * maxLen);
|
|
|
|
|
String str = randomString(len, variety);
|
|
|
|
|
int ans1 = restMin1(str);
|
|
|
|
|
int ans2 = restMin2(str);
|
|
|
|
|
int ans3 = restMin3(str);
|
|
|
|
|
if (ans1 != ans2 || ans1 != ans3) {
|
|
|
|
|
System.out.println(str);
|
|
|
|
|
System.out.println(ans1);
|
|
|
|
|
System.out.println(ans2);
|
|
|
|
|
System.out.println(ans3);
|
|
|
|
|
System.out.println("出错了!");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
System.out.println("测试结束");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|