Leetcode 132. Palindrome Partitioning II
题目
解法1:backtracking暴力解法(TLE)
class Solution:
def minCut(self, s: str) -> int:
def helper(i,count):
nonlocal ans
if i==len(s):
ans = min(ans,count-1)
for j in range(i,len(s)):
if s[i:j+1]==s[i:j+1][::-1]:
helper(j+1,count+1)
if s == s[::-1]:
return 0
ans = float('inf')
helper(0,0)
return ans
解法2:O(N^3)动态规划
递归方程是:dp[i] = min(dp[j-1]+1 for j in range(1,i+1) if s[j-1:i]==s[j-1:i][::-1])。
这个可以这么理解,dp[i]代表某个位置之前的字符串切分为回文子串需要的最小切分次数。我们遍历i之前所有位置,当s[j:i]否成一个回文串的时候,那么dp[i]和dp[i-j]的关系就定了,也就得到了subproblem。
class Solution:
def minCut(self, s: str) -> int:
dp = [float('inf')]*(len(s)+1)
dp[0] = -1
for i in range(1,len(s)+1):
for j in range(1,i+1):
if s[j-1:i] == s[j-1:i][::-1]:
dp[i] = min(dp[i],dp[j-1]+1)
return dp[-1]
解法3:O(N^2)动态规划
前面解法可以优化的地方在于,对于最后一段是不是回文,我们还是每次都对整段进行判断的。但当我们判断一个字符串是不是回文的时候,我们其实可以从字符串的中点出发,然后向外扩张,这样子每个子串是不是回文串就可以O(1)判断。所以这时候两层遍历外层遍历代表最后一个子串的中心,内层遍历代表子串长度的一半。需要格外注意的是,子串可能为奇数或者偶数,所以对于中心的判断需要分类讨论
class Solution {
public:
int INF = 1e9;
int minCut(string s) {
int n = s.length();
vector<int> dp(n+1,INF);
dp[0] = -1;
for (int i=0;i<n;++i){
// index i as the center
for (int j=0;i-j>=0 && i+j<n;++j){
if (s[i-j]!=s[i+j]){
break;
}
dp[i+j+1] = min(dp[i+j+1],dp[i-j]+1);
}
// index i, i+1 as a center
for (int j=0;i-j>=0 && i+1+j<n;++j){
if (s[i-j]!=s[i+1+j]){
break;
}
dp[i+j+2] = min(dp[i+j+2],dp[i-j]+1);
}
}
return dp[n];
}
};