2021/3/8
今天的每日一题是Leetcode132. 分割回文串 II,是昨天那道题的进阶版,题意如下:
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。
1 <= s.length <= 2000
首先看到这个题的数据范围是2000,用昨天的方法(昨天方法的复杂度为N*2^N)肯定会超时。这道题只需要求分割次数,并不需要找出每一种分割方法,所以可以不用昨天的暴力搜索方法。可以考虑之前的求最长递增子序列的方法,没有看过的可以去瞅一眼《[每日一题]Leetcode300. 最长递增子序列和Leetcode354. 俄罗斯套娃信封问题》。
这两道题的思路类似,都是使用动态规划的思想,在求最长递增子序列时dp[i]代表的是以i下标为结尾的最长子序列,同样这道题中dp[i]代表的是以i下标为结尾的最短回文串的分割次数。我们对每一个i遍历从0到i,用j表示,如果以j为划分点,j+1到i这后半段是一个回文串,那么dp[i]就等于dp[j]加上这一个字符串。所以状态转移方程为dp[i]=min(dp[i],dp[j]+1)。
同时这道题使用动态规划预处理判断回文串能减小时间复杂度,如果还不太明白可以去瞅一眼《[每日一题]Leetcode131. 分割回文串》。
c++参考代码:
class Solution {
private:
bool f[2005][2005];
public:
int minCut(string s) {
int n=s.size();
for (int j = 0; j < n; j++)
{
for (int i = j; i >= 0; i--)
{
if (i == j)
{
f[i][j] = true;
}
else
{
if (j - i + 1 == 2)
{
f[i][j]=s[i]==s[j];
}
else
{
f[i][j] = (s[i]==s[j] && f[i + 1][j - 1]);
}
}
}
}
vector<int> dp(n,n);//初值设为最大值n,最差情况为每个字符都分隔一次
for(int i=0;i<n;i++)
{
if(f[0][i])
{
dp[i]=0;
continue;
}
for(int j=0;j<i;j++)
{
if(f[j+1][i])
{
dp[i]=min(dp[i],dp[j]+1);
}
}
}
return dp[n-1];
}
};
时间复杂度:O(N^2)。
参考资料
·负雪明烛《相同的思路:从最长递增子序列谈起》