LeetCode 笔记24 Palindrome Partitioning II (智商碾压)
Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
For example, given s = "aab"
,
Return 1
since the palindrome partitioning ["aa","b"]
could be produced using 1 cut.
这种最小啊,最大啊,最长啊,etc肯定是用动态规划无疑了。
我之前已经知道如何判断一个字符串s中,s[i...j]是否是一个回文。
isPalindrome[i][j] = true iff s[i] == s[j] && isPalindrome[i + 1][j - 1] 或者 j - i <= 1
当我们得到这个matrix以后,我们可以认为这是一个邻接矩阵,也就是一个图。那么这个问题可以转换成一个源最短路径问题。那么最简单的就是用bfs了。
public int minCut(String s) { boolean[][] isPalindrome = new boolean[s.length() + 1][s.length() + 1]; // Map<Integer, Set<Integer>> adjecent = new HashMap<>(); for (int i = 0; i <= s.length(); i++) { isPalindrome[i][i] = true; // adjecent.put(i, new HashSet<Integer>()); } for (int i = s.length() - 1; i >= 0; i--) { for (int j = i + 1; j <= s.length(); j++) { isPalindrome[i][j] = s.charAt(i) == s.charAt(j - 1) && (j - i == 1 || isPalindrome[i + 1][j - 1]); if (isPalindrome[i][j]) { // adjecent.get(i).add(j); } } } LinkedList<Integer> queue = new LinkedList<Integer>(); boolean[] visited = new boolean[s.length() + 1]; queue.add(0); int head = 0; int depth = 0; while (!queue.isEmpty()) { int w = queue.poll(); if (head == w) { depth++; head = -1; } for (int i = 0; i < isPalindrome[w].length; i++) { if (isPalindrome[w][i] && i == s.length()) { return depth - 1; } if (isPalindrome[w][i] && !visited[i]) { visited[i] = true; queue.add(i); if (head == -1) { head = i; } } } } return -1; }
结果这个弄的很郁闷,因为之前我一直用的时hash map来表示邻接矩阵,结果老超时,于是干脆来粗暴的,直接使用得到的二维数组来做,竟然过了。hash map应该给我O(1)的时间才对啊,为啥还不如数组呢?
于是我google了一下,结果发现求最小的cut居然本身也可以用动态规划来做。算了,哭晕在厕所里。
先看代码。
public int minCut(String s) { boolean[][] isPalindrome = new boolean[s.length()][s.length()]; int[] cut = new int[s.length()]; for (int j = 0; j < s.length(); j++) { cut[j] = j; for (int i = 0; i <= j; i++) { if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || isPalindrome[i + 1][j - 1])) { isPalindrome[i][j] = true; if (i > 0) { cut[j] = Math.min(cut[j], cut[i - 1] + 1); } else { cut[j] = 0; } } } } return cut[s.length() - 1]; }
这区区几行就完了。可以观察到在求回文矩阵的部分是一样的。关键是那个cut数组。
cut[j]表示s[0...j]最小cut数。
在i移动的过程中,把i作为分割点。
那么 cut[j] = min(cut[i - 1] + 1) iif s[i...j] 是回文 for i = 0 ~ j
所以在最里面的一个循环(i 从 0 到j)中,既能求得isPalindrome[0...j][j],同时也能求得cut[j]了。
该算法还有一点,就是循环方向的选择很合理。