132.Palindrome Partitioning II
题目大意:给出一个字符串,对其进行划分,形成所有子串是回文串,找出最小需要划分的次数。比如字符串"aab",最小的划分次数是1,形成"aa","b",都是回文串。
法一:利用131题的DFS,超时。代码如下:
1 private static int dfs(String s, int res, int start, ArrayList<String> tmp) { 2 //如果找到一种划分情况,则将其接入结果集中 3 if(start == s.length()) { 4 if(tmp.size() - 1 < res) { 5 res = tmp.size() - 1; 6 } 7 return res; 8 } 9 //寻找每一个可能的回文字符串 10 for(int i = start; i < s.length(); i++) { 11 //判断字符串的其中一个子串,如果是回文 12 if(isPalindrome(s, start, i) == true) { 13 //将这个回文子串加入结果中,记住substring(start,end),是从start到end-1的字符串。 14 tmp.add(s.substring(start, i + 1)); 15 //注意,下次划分的子串起始点应该是i+1,因为i已经在上一个子串中了 16 res = dfs(s, res, i + 1, tmp); 17 //回溯移除 18 tmp.remove(tmp.size() - 1); 19 } 20 } 21 return res; 22 } 23 //判断回文 24 private static boolean isPalindrome(String s, int start, int end) { 25 while(start <= end) { 26 if(s.charAt(start) != s.charAt(end)) { 27 return false; 28 } 29 start++; 30 end--; 31 } 32 return true; 33 }
法二:用到了5题的DP,就是利用DP求解回文子字符串的问题,但是这题还扩展了,不仅求解回文子串,还要求解最小分割数,使得所有子串都是回文串,比如"aab",最后的DP数组就是,dp[0][0],dp[1][1],dp[2][2],这是一次分割;dp[0][1],dp[2][2],这是一次分割。可以看出dp[i][j]就是代表i到j的字符串是否是回文串,然后根据dp数组,再计算分割数。为了节约时间,两者放在一起同时计算,而分割数的dp方程是mi[i] = Math.min(mi[i], mi[j + 1] + 1)。mi[i]表示i到n-1的字符串需要分割的最小次数。代码如下(耗时20ms):
1 public int minCut(String s) { 2 //dp[i][j]表示在i到j之间的字符串是否是回文串 3 boolean[][] dp = new boolean[s.length()][s.length()]; 4 for(int i = 0; i < s.length(); i++) { 5 dp[i][i] = true; 6 } 7 //mi[i]表示i到n-1之间的字符串最小需要分割的次数 8 //从后往前计算 9 int[] mi = new int[s.length() + 1]; 10 for(int i = s.length() - 1; i >= 0; i--) { 11 mi[i] = Integer.MAX_VALUE;//初始化在i的后面分割一下 12 for(int j = i; j < s.length(); j++) { 13 if(s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1] == true)) { 14 dp[i][j] = true; 15 //因为i到j构成回文串,所以可以考虑在j后面分割,然后与在i后面分割进行比较 16 mi[i] = Math.min(mi[i], 1 + mi[j + 1]); 17 } 18 } 19 } 20 return mi[0] - 1; 21 }