NO.131 分割回文串 中等
思路一:回溯 就是暴力,逐位作为子串的起点,不同长度进行分割,是回文的子串的加入到一组结果中。过程中,对子串进行回文判断,不是回文的路径剪枝。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| List<List<String>> res = new ArrayList<>();
public List<List<String>> partition(String s) { if (s == null || s.equals("")) return res; dfs(s, 0, new LinkedList<String>()); return res; }
private void dfs(String s, int start, LinkedList<String> track) { if (start == s.length()) { res.add(new ArrayList<>(track)); return; } for (int i = start; i < s.length(); i++) { if (isPalindrome(s, start, i)) { track.add(s.substring(start, i + 1)); dfs(s, i + 1, track); track.removeLast(); } } }
private boolean isPalindrome(String s, int start, int end) { while (start < end) { if (s.charAt(start) != s.charAt(end)) { return false; } start++; end--; } return true; }
|
思路二:动态规划预处理 不再使用双指针判断[start,end]子串区间是否为回文,而是预处理生成dp数组,dp[left][right]表示[left,right]子串是否为回文串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| List<List<String>> res = new ArrayList<>();
public List<List<String>> partition(String s) { if (s == null || s.equals("")) return res; boolean[][] dp = new boolean[s.length()][s.length()]; for (int right = 0; right < s.length(); right++) { for (int left = 0; left <= right; left++) { dp[left][right] = s.charAt(right) == s.charAt(left) && (right - left <= 2 || dp[left + 1][right - 1]); } } dfs(s, 0, new LinkedList<String>(), dp); return res; }
private void dfs(String s, int start, LinkedList<String> track, boolean[][] dp) { if (start == s.length()) { res.add(new ArrayList<>(track)); return; } for (int i = start; i < s.length(); i++) { if (dp[start][i]) { track.add(s.substring(start, i + 1)); dfs(s, i + 1, track, dp); track.removeLast(); } } }
|
NO.132 分割回文串II 困难
思路一:动态规划
dp[right]的含义:以第right字符结尾的子串[0,right],符合要求的最少分割次数。
初始化:dp[right]=right,即初始化为最多分隔次数;过程中需要判断子串是否为回文,所以像NO.131中那样预处理得到一个标记子串是否为回文的dp数组。
状态转移:如果[0,right]区间子串是回文串,则不需要分割,即dp[right]=0;
否则right作为子串的右端点,枚举[0,right]区间内的left作为分隔点,如果[left+1,right]子串不是回文继续枚举下一个分隔点left,否则是回文则dp[right]=dp[left]+1,即在[0,left]的基础上多分割一次,枚举完毕所有的left取最小的dp[left]再+1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public int minCut(String s) { if (s == null || s.length() < 2) return 0; int len = s.length(); int[] dp = new int[len]; for (int right = 0; right < len; right++) { dp[right] = right; } boolean[][] isPalindrome = new boolean[len][len]; isPalindrome[0][0] = true; for (int right = 0; right < s.length(); right++) { for (int left = 0; left <= right; left++) { isPalindrome[left][right] = s.charAt(left) == s.charAt(right) && (right - left <= 2 || isPalindrome[left + 1][right - 1]); } } for (int right = 1; right < len; right++) { if (isPalindrome[0][right]) { dp[right] = 0; } else { for (int left = 0; left < right; left++) { if (isPalindrome[left + 1][right]) { dp[right] = Math.min(dp[right], dp[left] + 1); } } } } return dp[len - 1]; }
|
时间复杂度:O(n^2)
空间复杂度:O(n^2)
本人菜鸟,有错误请告知,感激不尽!
更多题解和源码:github