Welcome to Subscribe On Youtube
Question
Formatted question description: https://leetcode.ca/all/132.html
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
.
Example 1:
Input: s = "aab" Output: 1 Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
Example 2:
Input: s = "a" Output: 0
Example 3:
Input: s = "ab" Output: 1
Constraints:
1 <= s.length <= 2000
s
consists of lowercase English letters only.
Algorithm
One-dimensional dp array, where dp[i]
represents the minimum number of divisions in the range of substring [0, i]
.
Use a two-dimensional dp array p
, where p[i][j]
indicates whether the substring in the interval [i, j]
is a palindrome, and the state transition equation is p[i][j] = (s[i] == s[j]) && p[i+1][j-1]
, where p[i][j] = true
if [i, j]
is a palindrome.
Code
-
class Solution { public int minCut(String s) { int n = s.length(); boolean[][] dp1 = new boolean[n][n]; for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { dp1[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 3 || dp1[i + 1][j - 1]); } } int[] dp2 = new int[n]; for (int i = 0; i < n; i++) { if (!dp1[0][i]) { dp2[i] = i; for (int j = 1; j <= i; j++) { if (dp1[j][i]) { dp2[i] = Math.min(dp2[i], dp2[j - 1] + 1); } } } } return dp2[n - 1]; } } ////// public class Palindrome_Partitioning_II { public class Solution { public int minCut(String s) { if (s == null || s.length() == 0) { return -1; } // 'abcba', when reaching 'c', minCut=2 // when reaching 2nd 'b', minCut=1 // when reaching 2nd 'a', minCut=0 // so, if a substring is palindrome, cut is prev_segment+1 // set up a fast lookup for if palindrome from i to j boolean[][] isPal = new boolean[s.length()][s.length()]; for (int i = 0; i < s.length(); i++) { for (int j = i; j >= 0; j--) { if (i == j || (s.charAt(i) == s.charAt(j) && (i - j == 1 || isPal[j + 1][i - 1]))) { isPal[j][i] = true; // j is first, then i } } } // set up min count array, worst case, each char is a palindrome int[] minCut = new int[s.length()]; for (int i = 0; i < s.length(); i++) { minCut[i] = i; } // how to cound, eg: 'aabcba', 'aabcbaz' // @note: if a substring is palindrome, cut is prev_segment+1 for (int i = 0; i < s.length(); i++) { for (int j = 0; j < s.length(); j++) { if (isPal[i][j]) { if (i - 1 >= 0) { // @note: eg, 'aabcba' minCut[j] = Math.min(minCut[j], 1 + minCut[i - 1]); } else { // i.e., i==0. eg, 'abcba' minCut[j] = 0; } } } } int min = minCut[s.length() - 1]; return min; } } }
-
class Solution { public: int minCut(string s) { int n = s.size(); vector<vector<bool>> dp1(n, vector<bool>(n)); for (int i = n - 1; i >= 0; --i) { for (int j = i; j < n; ++j) { dp1[i][j] = s[i] == s[j] && (j - i < 3 || dp1[i + 1][j - 1]); } } vector<int> dp2(n); for (int i = 0; i < n; ++i) { if (!dp1[0][i]) { dp2[i] = i; for (int j = 1; j <= i; ++j) { if (dp1[j][i]) { dp2[i] = min(dp2[i], dp2[j - 1] + 1); } } } } return dp2[n - 1]; } };
-
class Solution: def minCut(self, s: str) -> int: pal = [[False for j in range(0, len(s))] for i in range(0, len(s))] dp = [len(s) for _ in range(0, len(s) + 1)] for i in range(0, len(s)): for j in range(0, i + 1): # also ok if, (j + 1 >= i - 1) if (s[i] == s[j]) and ((j + 1 > i - 1) or (pal[i - 1][j + 1])): pal[i][j] = True dp[i + 1] = min(dp[i + 1], dp[j] + 1) if j != 0 else 0 # 'if j != 0' to ensure from start to i is a palindrome return dp[-1] ############ class Solution: # clear cache def minCut(self, s: str) -> int: @cache def dfs(i): if i >= n - 1: return 0 ans = inf for j in range(i, n): if g[i][j]: ans = min(ans, dfs(j + 1) + (j < n - 1)) return ans n = len(s) g = [[True] * n for _ in range(n)] for i in range(n - 1, -1, -1): for j in range(i + 1, n): g[i][j] = s[i] == s[j] and g[i + 1][j - 1] ans = dfs(0) dfs.cache_clear() # nice, clear cache !!! return ans ############ class Solution: def minCut(self, s: str) -> int: n = len(s) is_pal = [[False] * n for _ in range(n)] for i in range(n - 1, -1, -1): for j in range(i, n): is_pal[i][j] = s[i] == s[j] and (j - i < 3 or is_pal[i + 1][j - 1]) min_cut = [i for i in range(n)] for i in range(n): for j in range(0, i + 1): if is_pal[j][i]: min_cut[i] = min(min_cut[i], min_cut[j - 1] + 1) if j > 0 else 0 return min_cut[-1]
-
func minCut(s string) int { n := len(s) dp1 := make([][]bool, n) for i := 0; i < n; i++ { dp1[i] = make([]bool, n) } for i := n - 1; i >= 0; i-- { for j := i; j < n; j++ { dp1[i][j] = s[i] == s[j] && (j-i < 3 || dp1[i+1][j-1]) } } dp2 := make([]int, n) for i := 0; i < n; i++ { if !dp1[0][i] { dp2[i] = i for j := 1; j <= i; j++ { if dp1[j][i] { dp2[i] = min(dp2[i], dp2[j-1]+1) } } } } return dp2[n-1] } func min(x, y int) int { if x < y { return x } return y }
-
using System; using System.Collections.Generic; public class Solution { public int MinCut(string s) { if (s.Length == 0) return 0; var paths = new List<int>[s.Length]; for (var i = 0; i < s.Length; ++i) { int j, k; for (j = i, k = i + 1; j >= 0 && k < s.Length; --j, ++k) { if (s[j] == s[k]) { if (paths[k] == null) { paths[k] = new List<int>(); } paths[k].Add(j - 1); } else { break; } } for (j = i, k = i; j >= 0 && k < s.Length; --j, ++k) { if (s[j] == s[k]) { if (paths[k] == null) { paths[k] = new List<int>(); } paths[k].Add(j - 1); } else { break; } } } var partCount = new int[s.Length]; for (var i = 0; i < s.Length; ++i) { partCount[i] = int.MaxValue; if (paths[i] != null) { foreach (var path in paths[i]) { if (path < 0) { partCount[i] = 0; break; } else { partCount[i] = Math.Min(partCount[i], partCount[path]); } } } ++partCount[i]; } return partCount[s.Length - 1] - 1; } }
-
function minCut(s: string): number { const n = s.length; const g: boolean[][] = Array(n) .fill(0) .map(() => Array(n).fill(true)); for (let i = n - 1; ~i; --i) { for (let j = i + 1; j < n; ++j) { g[i][j] = s[i] === s[j] && g[i + 1][j - 1]; } } const f: number[] = Array(n) .fill(0) .map((_, i) => i); for (let i = 1; i < n; ++i) { for (let j = 0; j <= i; ++j) { if (g[j][i]) { f[i] = Math.min(f[i], j ? 1 + f[j - 1] : 0); } } } return f[n - 1]; }