leetcode | 132. Palindrome Partitioning II
阿新 • • 發佈:2018-12-09
題目
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:
Input: "aab"
Output: 1
Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
思路與解法
此題目要求我們計算將字串s
對於題目中的樣例字串
"aab"
,我們想要得到該字串的最小切分次數,首先需要判斷字串是否為迴文序列,如果該字串為迴文序列,那麼萬事大吉。然而,大多數情況下輸入字串是不可能為迴文序列的,所以我們需要進行切分操作,即我們需要找到該字串的連續迴文子串的最少個數,但是最開始我們需要得到一個字串是否滿足迴文。我們可以採用動態規劃的思想來解決該問題,首先定義以下dp陣列:
dp[i][j]
表示從s[i:j+1]
(左閉右開)是否為迴文序列,如果是迴文序列,則dp[i][j]=1
;否則dp[i][j]=0
。同時,可以推出狀態轉移方程為:
// 情況1:i+1 == j (長度等於2的子串)
if s[i] == s[j] {
dp[i][j] = 1
}
// 情況1:i+1 < j (長度大於2的子串)
if dp[i+1][j-1] == 1 && s[i] == s[j] {
dp[i][j] = 1
}
此時,我們得到了字串s的所有子串是否滿足迴文的dp
陣列:時間複雜度為
。相比較於迴圈遍歷字串並首尾判斷迴文的方法要快上許多,後者複雜度為
。
之後,我們需要計算將該字串切分所需要的最少切分數,定義如下陣列,minCuts[i]
表示字串s[:i](前i個字元,minCuts下標從1~len(s),與dp陣列下標範圍不同)切分為迴文子串的最小切分數,則狀態轉移方程為:
// 下屬if條件句中,j<i
// dp[j-1][i-1]為1,表示s[j-1:i-1]為迴文序列
if dp[j-1][i-1] == 1 && minCuts[i] > minCuts[j-1] + 1 {
minCuts[i] = minCuts[j-1] + 1
}
程式碼實現(Go)
const INT_MAX = int(^uint(0) >> 1)
func minCut(s string) int {
lenS := len(s)
dp := make([][]int, lenS)
minCuts := make([]int, lenS+10)
// 初始化dp陣列,dp[i][i]=1
// 初始化minCuts[2~lenS] = INT_MAX
for i:=0; i<lenS; i++ {
dp[i] = make([]int, lenS)
dp[i][i] = 1
minCuts[i+1] = INT_MAX
}
// 長度為0的字串最小切分數設定為-1;長度為1的字串最小切分數設定為0
minCuts[0] = -1
minCuts[1] = 0
// 注意迴圈的順序,j在外層
// 簡單講是因為dp[0][lenS]應該在已知dp[1][lenS-1]後在計算獲得
for j:=0; j<lenS; j++ {
for i:=0; i<lenS; i++ {
if j > i {
if dp[i+1][j-1]==1 && s[i] == s[j] {
dp[i][j] = 1
} else if i+1==j && s[i] == s[j] {
dp[i][j] = 1
}
}
}
}
// 獲得minCuts,為方便處理邊界,我將minCuts的下標定義為1~lenS
for i:=1; i<=lenS; i++ {
for j:=1; j<=i; j++ {
if dp[j-1][i-1] == 1 && minCuts[i] > minCuts[j-1] + 1 {
minCuts[i] = minCuts[j-1] + 1
}
}
}
return minCuts[lenS]
}
執行結果
總體時間複雜度為
:
遞迴解法
最初我採用遞迴來實現(dp陣列計算方法同上),遞迴更加容易理解,我們在計算s的最小切分數時,求得s[:k]
、s[k:]
(其中1<=k<lenS
)最小切分數之和得最小值,即為s得最小切分數。
for k:=i+1; k<=j; k++ {
cuts1 := getMinCuts(i, k-1, s)
cuts2 := getMinCuts(k, j, s)
if cuts1 + cuts2 + 1 < minCuts {
minCuts = cuts1 + cuts2 + 1
}
}
但是,上述方法中做了許多無用的計算(並不能通過後三組資料),不滿足dp[i][k-1]==1
得計算時多餘的,所以我進行了改進:
const INT_MAX = int(^uint(0) >> 1)
var cutsMap map[string]int
var dp [][]int
func getMinCuts(i, j int, s string) int{
if cutsMap[s[i:j+1]]!=0 {
return cutsMap[s[i:j+1]]
}
if dp[i][j] == 1 {
return 0
}
minCuts := INT_MAX
for k:=i+1; k<=j; k++ {
if dp[i][k-1] == 1 {
cuts := getMinCuts(k, j, s)
if cuts + 1 < minCuts {
minCuts = cuts + 1
}
}
}
cutsMap[s[i:j+1]] = minCuts
return minCuts
}
func minCut(s string) int {
cutsMap = make(map[string]int)
lenS := len(s)
dp = make([][]int, lenS)
for i:=0; i<lenS; i++ {
dp[i] = make([]int, lenS)
dp[i][i] = 1
}
for j:=0; j<lenS; j++ {
for i:=0; i<lenS; i++ {
if j > i {
if dp[i+1][j-1]==1 && s[i] == s[j] {
dp[i][j] = 1
} else if i+1==j && s[i] == s[j] {
dp[i][j] = 1
}
}
}
}
return getMinCuts(0, lenS-1, s)
}