Welcome to Subscribe On Youtube
Question
Formatted question description: https://leetcode.ca/all/131.html
Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s.
Example 1:
Input: s = "aab" Output: [["a","a","b"],["aa","b"]]
Example 2:
Input: s = "a" Output: [["a"]]
Constraints:
1 <= s.length <= 16scontains only lowercase English letters.
Algorithm
It employs dynamic programming (DP) to identify all substrings that are palindromes and then uses depth-first search (DFS) to explore all possible partitioning combinations that consist exclusively of palindrome substrings.
Dynamic Programming (DP) Phase
-
Initialization: A 2D list
dpof sizen x nis created, wherenis the length of the input strings. Each elementdp[i][j]indicates whether the substrings[i:j+1]is a palindrome. -
DP Logic: The code fills the
dptable in reverse order (starting from the end of the string and moving towards the beginning). This reverse order ensures that when checking ifs[i:j+1]is a palindrome (ands[i] == s[j]), the result of the inner substrings[i+1:j]is already calculated and stored indp[i+1][j-1]. -
Palindrome Conditions: A substring
s[i:j+1]is considered a palindrome if the start and end characters are equal (s[i] == s[j]) and either of the following conditions is true:- The substring’s length is 2 or less (
abs(i - j) <= 1), meaning it’s either a single character or two identical characters. - The inner substring
s[i+1:j]is a palindrome (dp[i+1][j-1]isTrue).
- The substring’s length is 2 or less (
Depth-First Search (DFS) Phase
-
Recursive DFS Function: The function
dfsrecursively explores all possible ways to partition the stringsstarting from indexiinto palindrome substrings. It accumulates partitions in a temporary listtand adds a copy oftto the final answeranswhen it reaches the end of the string. -
DFS Logic: For each position
iin the string, the function iterates over all possible end indicesj(fromiton-1). If the substrings[i:j+1]is a palindrome (dp[i][j]isTrue), it adds this substring to the current partitiontand recurses to the next positionj+1. After exploring all partitions starting withs[i:j+1], it removes the last added substring fromtto backtrack and explore other possibilities. -
Base Case: When
iequals the length of the string (n), it means a valid partitioning ofsinto palindrome substrings is found, and a copy of the current partitiontis added toans.
Final Output
- The solution initiates the DFS search with
dfs(s, 0, []), starting from the beginning of the string and an empty partition list. After exploring all possible partitioning, it returnsans, which contains all the valid palindrome partitioning ofs.
This approach smartly combines dynamic programming to efficiently identify palindromes within the string and depth-first search to explore all partitioning possibilities, ensuring a comprehensive solution to the palindrome partitioning problem.
Code
-
import java.util.ArrayList; import java.util.List; public class Palindrome_Partitioning { public class Solution_dp { public List<List<String>> partition(String s) { int n = s.length(); List<List<String>> res = new ArrayList<>(); List<String> out = new ArrayList<>(); boolean[][] dp = new boolean[n][n]; for (int i = 0; i < n; ++i) { for (int j = 0; j <= i; ++j) { if (s.charAt(i) == s.charAt(j) && (i - j <= 2 || dp[j + 1][i - 1])) { dp[j][i] = true; } } } helper(s, 0, dp, out, res); return res; } void helper(String s, int start, boolean[][] dp, List<String> out, List<List<String>> res) { if (start == s.length()) { res.add(new ArrayList<>(out)); return; } for (int i = start; i < s.length(); ++i) { if (!dp[start][i]) continue; out.add(s.substring(start, i + 1)); helper(s, i + 1, dp, out, res); out.remove(out.size() - 1); } } } // based on part-I, just count each partition to find min... public class Solution_over_time { List<List<String>> list = new ArrayList<>(); public List<List<String>> partition(String s) { if (s == null || s.length() == 0) { return list; } find(s, new ArrayList<String>()); return list; } private void find(String s, ArrayList<String> currentList) { if (s.length() == 0) { list.add(currentList); return; } // idea is, scan from index=0, to find each palindrome, then rest substring to next recursion for (int i = 0; i < s.length(); i++) { String sub = s.substring(0, i + 1); // @note: substring 0-s[i] // System.out.println("substring is: " + sub); if (isPal(sub)) { ArrayList<String> nextList = new ArrayList<>(currentList); // deep copy nextList.add(sub); find(s.substring(i + 1), nextList); } } } private boolean isPal(String s) { int i = 0; int j = s.length() - 1; while (i <= j) { // @note: better in if (s.charAt(i++) != s.charAt(j--)) { // @note: 忘了这里的++和--。。。 if (s.charAt(i) != s.charAt(j)) { return false; } i++; j--; } return true; } } } ############ class Solution { private boolean[][] dp; private List<List<String>> ans; private int n; public List<List<String>> partition(String s) { ans = new ArrayList<>(); n = s.length(); dp = new boolean[n][n]; for (int i = 0; i < n; ++i) { Arrays.fill(dp[i], true); } for (int i = n - 1; i >= 0; --i) { for (int j = i + 1; j < n; ++j) { dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]; } } dfs(s, 0, new ArrayList<>()); return ans; } private void dfs(String s, int i, List<String> t) { if (i == n) { ans.add(new ArrayList<>(t)); return; } for (int j = i; j < n; ++j) { if (dp[i][j]) { t.add(s.substring(i, j + 1)); dfs(s, j + 1, t); t.remove(t.size() - 1); } } } } -
// OJ: https://leetcode.com/problems/palindrome-partitioning/ // Time: O(N * 2^N) // Space: O(N) extra space class Solution { vector<vector<string>> ans; vector<string> tmp; bool isPalindrome(string &s, int i, int j) { while (i < j && s[i] == s[j]) ++i, --j; return i >= j; } void dfs(string &s, int start) { if (start == s.size()) { ans.push_back(tmp); return; } for (int i = start; i < s.size(); ++i) { if (!isPalindrome(s, start, i)) continue; tmp.push_back(s.substr(start, i - start + 1)); dfs(s, i + 1); tmp.pop_back(); } } public: vector<vector<string>> partition(string s) { dfs(s, 0); return ans; } }; -
''' >>> t = [] >>> t.append(1) >>> t.append(2) >>> t.append(3) >>> t [1, 2, 3] >>> t.pop(-1) 3 >>> t [1, 2] >>> t.pop() 2 >>> t [1] >>> t = [1,2,3] >>> t.pop(0) 1 >>> t [2, 3] >>> t = [1,2,3] >>> t.pop(1) 2 >>> t [1, 3] ''' class Solution: def partition(self, s: str) -> List[List[str]]: ans = [] n = len(s) dp = [[False] * n for _ in range(n)] for i in range(n - 1, -1, -1): for j in range(i, n): # <=3 also working, same as <=1 # i==j, or i+1==j dp[i][j] = s[i] == s[j] and (abs(i - j) <= 1 or dp[i + 1][j - 1]) def dfs(s, i, t): nonlocal n # note: still pass OJ without this nonlocal. default is non-local if i == n: ans.append(t.copy()) return for j in range(i, n): # including single char dp[i][i] if dp[i][j]: t.append(s[i : j + 1]) dfs(s, j + 1, t) t.pop(-1) dfs(s, 0, []) return ans ########### class Solution(object): # iteration, real dp def partition(self, s): """ :type s: str :rtype: List[List[str]] """ pal = [[False for i in range(0, len(s))] for j in range(0, len(s))] ans = [[[]]] + [[] for _ in range(len(s))] # length is n+1 for i in range(0, len(s)): for j in range(0, i + 1): if (s[j] == s[i]) and ((j + 1 > i - 1) or (pal[j + 1][i - 1])): pal[j][i] = True for res in ans[j]: a = res + [s[j:i + 1]] ans[i + 1].append(a) return ans[-1] -
func partition(s string) [][]string { n := len(s) dp := make([][]bool, n) var ans [][]string for i := 0; i < n; i++ { dp[i] = make([]bool, n) for j := 0; j < n; j++ { dp[i][j] = true } } for i := n - 1; i >= 0; i-- { for j := i + 1; j < n; j++ { dp[i][j] = s[i] == s[j] && dp[i+1][j-1] } } var dfs func(s string, i int, t []string) dfs = func(s string, i int, t []string) { if i == n { ans = append(ans, append([]string(nil), t...)) return } for j := i; j < n; j++ { if dp[i][j] { t = append(t, s[i:j+1]) dfs(s, j+1, t) t = t[:len(t)-1] } } } var t []string dfs(s, 0, t) return ans } -
using System.Collections.Generic; using System.Linq; public class Solution { public IList<IList<string>> Partition(string s) { if (s.Length == 0) return new List<IList<string>>(); 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 prevs = new List<int>[s.Length]; for (var i = 0; i < s.Length; ++i) { if (paths[i] != null) { foreach (var path in paths[i]) { if (path < 0 || prevs[path] != null) { if (prevs[i] == null) { prevs[i] = new List<int>(); } prevs[i].Add(path); } } } } var results = new List<IList<string>>(); var temp = new List<string>(); GenerateResults(prevs, s, s.Length - 1, temp, results); return results; } private void GenerateResults(List<int>[] prevs, string s, int i, IList<string> temp, IList<IList<string>> results) { if (i < 0) { results.Add(temp.Reverse().ToList()); } else { foreach (var prev in prevs[i]) { temp.Add(s.Substring(prev + 1, i - prev)); GenerateResults(prevs, s, prev, temp, results); temp.RemoveAt(temp.Count - 1); } } } } -
function partition(s: string): string[][] { const n = s.length; const f: boolean[][] = new Array(n) .fill(0) .map(() => new Array(n).fill(true)); for (let i = n - 1; i >= 0; --i) { for (let j = i + 1; j < n; ++j) { f[i][j] = s[i] === s[j] && f[i + 1][j - 1]; } } const ans: string[][] = []; const t: string[] = []; const dfs = (i: number) => { if (i === n) { ans.push(t.slice()); return; } for (let j = i; j < n; ++j) { if (f[i][j]) { t.push(s.slice(i, j + 1)); dfs(j + 1); t.pop(); } } }; dfs(0); return ans; }