# Palindrome Partitioning II
- tags: [[DP_Sequence](# "单序列动态规划,通常使用 f[i] 表示前i个位置/数字/字母... 使用 f[n-1] 表示最后返回结果。")]
### Source
- leetcode: [Palindrome Partitioning II | LeetCode OJ](https://leetcode.com/problems/palindrome-partitioning-ii/)
- lintcode: [(108) Palindrome Partitioning II](http://www.lintcode.com/en/problem/palindrome-partitioning-ii/)
~~~
Given a string s, cut s into some substrings such that
every substring is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
Example
For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced
using 1 cut.
~~~
### 题解1 - 仅对最小切割数使用动态规划
此题为难题,费了我九牛二虎之力才bug-free :( 求最小切分数,非常明显的动规暗示。由问题出发可建立状态`f[i]` 表示到索引`i` 处时需要的最少切割数(即切割前 i 个字符组成的字符串),状态转移方程为`f[i] = min{1 + f[j]}, where j < i and substring [j, i] is palindrome`, 判断区间[j, i] 是否为回文简单的方法可反转后比较。
### Python
~~~
class Solution:
# @param s, a string
# @return an integer
def minCut(self, s):
if not s:
print 0
cut = [i - 1 for i in xrange(1 + len(s))]
for i in xrange(1 + len(s)):
for j in xrange(i):
# s[j:i] is palindrome
if s[j:i] == s[j:i][::-1]:
cut[i] = min(cut[i], 1 + cut[j])
return cut[-1]
~~~
### 源码分析
1. 当 s 为 None 或者列表为空时返回0
1. 初始化切割数数组
1. 子字符串的索引位置可为`[0, len(s) - 1]`, 内循环 j 比外循环 i 小,故可将 i 的最大值设为`1 + len(s)` 较为便利。
1. 回文的判断使用了`[::-1]` 对字符串进行反转
1. 最后返回数组最后一个元素
### 复杂度分析
两重循环,遍历的总次数为 1/2⋯n2)1/2 \cdots n^2)1/2⋯n2), 每次回文的判断时间复杂度为 O(len(s))O(len(s))O(len(s)), 故总的时间复杂度近似为 O(n3)O(n^3)O(n3). 在 s 长度较长时会 [TLE](# "Time Limit Exceeded 的简称。你的程序在 OJ 上的运行时间太长了,超过了对应题目的时间限制。").使用了与 s 等长的辅助切割数数组,空间复杂度近似为 O(n)O(n)O(n).
### 题解2 - 使用动态规划计算子字符串回文状态
切割数部分使用的是动态规划,优化的空间不大,仔细瞅瞅可以发现在判断字符串是否为回文的部分存在大量重叠计算,故可引入动态规划进行优化,时间复杂度可优化至到平方级别。
定义状态 PaMat[i][j] 为区间 `[i,j]` 是否为回文的标志, 对应此状态的子问题可从回文的定义出发,如果字符串首尾字符相同且在去掉字符串首尾字符后字符串仍为回文,则原字符串为回文,相应的状态转移方程 `PaMat[i][j] = s[i] == s[j] && PaMat[i+1][j-1]`, 由于状态转移方程中依赖比`i`大的结果,故实现中需要从索引大的往索引小的递推,另外还需要考虑一些边界条件和初始化方式,做到 bug-free 需要点时间。
### Python
~~~
class Solution:
# @param s, a string
# @return an integer
def minCut(self, s):
if not s:
print 0
cut = [i - 1 for i in xrange(1 + len(s))]
PaMatrix = self.getMat(s)
for i in xrange(1 + len(s)):
for j in xrange(i):
if PaMatrix[j][i - 1]:
cut[i] = min(cut[i], cut[j] + 1)
return cut[-1]
def getMat(self, s):
PaMat = [[True for i in xrange(len(s))] for j in xrange(len(s))]
for i in xrange(len(s), -1, -1):
for j in xrange(i, len(s)):
if j == i:
PaMat[i][j] = True
# not necessary if init with True
# elif j == i + 1:
# PaMat[i][j] = s[i] == s[j]
else:
PaMat[i][j] = s[i] == s[j] and PaMat[i + 1][j - 1]
return PaMat
~~~
### C++
~~~
class Solution {
public:
int minCut(string s) {
if (s.empty()) return 0;
int len = s.size();
vector<int> cut;
for (int i = 0; i < 1 + len; ++i) {
cut.push_back(i - 1);
}
vector<vector<bool> > mat = getMat(s);
for (int i = 1; i < 1 + len; ++i) {
for (int j = 0; j < i; ++j) {
if (mat[j][i - 1]) {
cut[i] = min(cut[i], 1 + cut[j]);
}
}
}
return cut[len];
}
vector<vector<bool> > getMat(string s) {
int len = s.size();
vector<vector<bool> > mat = vector<vector<bool> >(len, vector<bool>(len, true));
for (int i = len; i >= 0; --i) {
for (int j = i; j < len; ++j) {
if (j == i) {
mat[i][j] = true;
} else if (j == i + 1) {
mat[i][j] = (s[i] == s[j]);
} else {
mat[i][j] = ((s[i] == s[j]) && mat[i + 1][j - 1]);
}
}
}
return mat;
}
};
~~~
### Java
~~~
public class Solution {
public int minCut(String s) {
if (s == null || s.length() == 0) return 0;
int len = s.length();
int[] cut = new int[1 + len];
for (int i = 0; i < 1 + len; ++i) {
cut[i] = i - 1;
}
boolean[][] mat = paMat(s);
for (int i = 1; i < 1 + len; i++) {
for (int j = 0; j < i; j++) {
if (mat[j][i - 1]) {
cut[i] = Math.min(cut[i], 1 + cut[j]);
}
}
}
return cut[len];
}
private boolean[][] paMat(String s) {
int len = s.length();
boolean[][] mat = new boolean[len][len];
for (int i = len - 1; i >= 0; i--) {
for (int j = i; j < len; j++) {
if (j == i) {
mat[i][j] = true;
} else if (j == i + 1) {
mat[i][j] = (s.charAt(i) == s.charAt(j));
} else {
mat[i][j] = (s.charAt(i) == s.charAt(j)) && mat[i + 1][j - 1];
}
}
}
return mat;
}
}
~~~
### 源码分析
初始化 cut 长度为`1 + len(s)`, `cut[0] = -1` 便于状态转移方程实现。在执行`mat[i][j] == ... mat[i + 1][j - 1]`时前提是`j - 1 > i + 1`, 所以才需要分情况赋值。使用`getMat` 得到字符串区间的回文矩阵,由于cut 的长度为1+len(s), 两重 for 循环时需要注意索引的取值,这个地方非常容易错。
### 复杂度分析
最坏情况下每次 for 循环都遍历 n 次,时间复杂度近似为 O(n2)O(n^2)O(n2), 使用了二维回文矩阵保存记忆化搜索结果,空间复杂度为 O(n2)O(n^2)O(n2).
### Reference
- [Palindrome Partitioning II 参考程序 Java/C++/Python](http://www.jiuzhang.com/solutions/palindrome-partitioning-ii/)
- soulmachine 的 leetcode 题解
- Preface
- Part I - Basics
- Basics Data Structure
- String
- Linked List
- Binary Tree
- Huffman Compression
- Queue
- Heap
- Stack
- Set
- Map
- Graph
- Basics Sorting
- Bubble Sort
- Selection Sort
- Insertion Sort
- Merge Sort
- Quick Sort
- Heap Sort
- Bucket Sort
- Counting Sort
- Radix Sort
- Basics Algorithm
- Divide and Conquer
- Binary Search
- Math
- Greatest Common Divisor
- Prime
- Knapsack
- Probability
- Shuffle
- Basics Misc
- Bit Manipulation
- Part II - Coding
- String
- strStr
- Two Strings Are Anagrams
- Compare Strings
- Anagrams
- Longest Common Substring
- Rotate String
- Reverse Words in a String
- Valid Palindrome
- Longest Palindromic Substring
- Space Replacement
- Wildcard Matching
- Length of Last Word
- Count and Say
- Integer Array
- Remove Element
- Zero Sum Subarray
- Subarray Sum K
- Subarray Sum Closest
- Recover Rotated Sorted Array
- Product of Array Exclude Itself
- Partition Array
- First Missing Positive
- 2 Sum
- 3 Sum
- 3 Sum Closest
- Remove Duplicates from Sorted Array
- Remove Duplicates from Sorted Array II
- Merge Sorted Array
- Merge Sorted Array II
- Median
- Partition Array by Odd and Even
- Kth Largest Element
- Binary Search
- Binary Search
- Search Insert Position
- Search for a Range
- First Bad Version
- Search a 2D Matrix
- Search a 2D Matrix II
- Find Peak Element
- Search in Rotated Sorted Array
- Search in Rotated Sorted Array II
- Find Minimum in Rotated Sorted Array
- Find Minimum in Rotated Sorted Array II
- Median of two Sorted Arrays
- Sqrt x
- Wood Cut
- Math and Bit Manipulation
- Single Number
- Single Number II
- Single Number III
- O1 Check Power of 2
- Convert Integer A to Integer B
- Factorial Trailing Zeroes
- Unique Binary Search Trees
- Update Bits
- Fast Power
- Hash Function
- Count 1 in Binary
- Fibonacci
- A plus B Problem
- Print Numbers by Recursion
- Majority Number
- Majority Number II
- Majority Number III
- Digit Counts
- Ugly Number
- Plus One
- Linked List
- Remove Duplicates from Sorted List
- Remove Duplicates from Sorted List II
- Remove Duplicates from Unsorted List
- Partition List
- Two Lists Sum
- Two Lists Sum Advanced
- Remove Nth Node From End of List
- Linked List Cycle
- Linked List Cycle II
- Reverse Linked List
- Reverse Linked List II
- Merge Two Sorted Lists
- Merge k Sorted Lists
- Reorder List
- Copy List with Random Pointer
- Sort List
- Insertion Sort List
- Check if a singly linked list is palindrome
- Delete Node in the Middle of Singly Linked List
- Rotate List
- Swap Nodes in Pairs
- Remove Linked List Elements
- Binary Tree
- Binary Tree Preorder Traversal
- Binary Tree Inorder Traversal
- Binary Tree Postorder Traversal
- Binary Tree Level Order Traversal
- Binary Tree Level Order Traversal II
- Maximum Depth of Binary Tree
- Balanced Binary Tree
- Binary Tree Maximum Path Sum
- Lowest Common Ancestor
- Invert Binary Tree
- Diameter of a Binary Tree
- Construct Binary Tree from Preorder and Inorder Traversal
- Construct Binary Tree from Inorder and Postorder Traversal
- Subtree
- Binary Tree Zigzag Level Order Traversal
- Binary Tree Serialization
- Binary Search Tree
- Insert Node in a Binary Search Tree
- Validate Binary Search Tree
- Search Range in Binary Search Tree
- Convert Sorted Array to Binary Search Tree
- Convert Sorted List to Binary Search Tree
- Binary Search Tree Iterator
- Exhaustive Search
- Subsets
- Unique Subsets
- Permutations
- Unique Permutations
- Next Permutation
- Previous Permuation
- Unique Binary Search Trees II
- Permutation Index
- Permutation Index II
- Permutation Sequence
- Palindrome Partitioning
- Combinations
- Combination Sum
- Combination Sum II
- Minimum Depth of Binary Tree
- Word Search
- Dynamic Programming
- Triangle
- Backpack
- Backpack II
- Minimum Path Sum
- Unique Paths
- Unique Paths II
- Climbing Stairs
- Jump Game
- Word Break
- Longest Increasing Subsequence
- Palindrome Partitioning II
- Longest Common Subsequence
- Edit Distance
- Jump Game II
- Best Time to Buy and Sell Stock
- Best Time to Buy and Sell Stock II
- Best Time to Buy and Sell Stock III
- Best Time to Buy and Sell Stock IV
- Distinct Subsequences
- Interleaving String
- Maximum Subarray
- Maximum Subarray II
- Longest Increasing Continuous subsequence
- Longest Increasing Continuous subsequence II
- Graph
- Find the Connected Component in the Undirected Graph
- Route Between Two Nodes in Graph
- Topological Sorting
- Word Ladder
- Bipartial Graph Part I
- Data Structure
- Implement Queue by Two Stacks
- Min Stack
- Sliding Window Maximum
- Longest Words
- Heapify
- Problem Misc
- Nuts and Bolts Problem
- String to Integer
- Insert Interval
- Merge Intervals
- Minimum Subarray
- Matrix Zigzag Traversal
- Valid Sudoku
- Add Binary
- Reverse Integer
- Gray Code
- Find the Missing Number
- Minimum Window Substring
- Continuous Subarray Sum
- Continuous Subarray Sum II
- Longest Consecutive Sequence
- Part III - Contest
- Google APAC
- APAC 2015 Round B
- Problem A. Password Attacker
- Microsoft
- Microsoft 2015 April
- Problem A. Magic Box
- Problem B. Professor Q's Software
- Problem C. Islands Travel
- Problem D. Recruitment
- Microsoft 2015 April 2
- Problem A. Lucky Substrings
- Problem B. Numeric Keypad
- Problem C. Spring Outing
- Microsoft 2015 September 2
- Problem A. Farthest Point
- Appendix I Interview and Resume
- Interview
- Resume