leetcode每日一道(17)思路惊为天人!切分为回文子串所需的最少切分次数

题目描述

给出一个字符串s,分割s使得分割出的每一个子串都是回文串
计算将字符串s分割成回文分割结果的最小切割数
例如:给定字符串s=“aab”,
返回1,因为回文分割结果[“aa”,“b”]是切割一次生成的。

思路

这道题如果不利用动态规划的话,就相当于再每两个字符之间,都有切分和不切分两种选择,那么时间复杂度是O(2n1)O(2^{n-1})的,也就是O(2n)O(2^n)的,暴力求解是没有出路的。考虑用动态规划的方法。那么动态规划的方法啊,第一步就是定义dp数组的含义。

  1. 定义数组的含义
    我们要尽量使得我们定义的含义和题目要求的结果(最小切割数)一致,这样比较合理,因此我们定义dp[i]的含义为:从下标ii的位置到末尾,需要的最小切割数。最终的答案,当然就是dp[0]了,为什么要这样定义?可以想想,我们可不可以换种定义方法:dp[i]表示从开头到 i 的位置,需要的最小切割数,那么最后应返回的结果就是dp[s.length()-1]。这样不是不可以,但是会麻烦一点,我们后面会说,为什么要麻烦一点。

  2. 找到递推关系
    动态规划最重要的就是找递推关系了,我们可以肯定的是,这道题,一层遍历指定是无法完成的,最后优化后的复杂度肯定是O(n2)O(n^2),我们的第一层遍历是 i 从末尾开始,一直往前,第二层遍历就是:j 从i 开始,一直往后。那么递推关系就有点考究了
    我们想想,如果i 到j 是回文串的话,那么岂不是dp[i] = dp[j+1] +1 ,为什么要加1呢,因为我们首先需要在j 后面切分一刀,这个式子可以理解一下,其实写出来就不难了,难的是你如何想到这个式子,那么遍历所有的j , 我们有:dp[i] = min(dp[j+1] +1)

  3. 再定义个数组,去冗余计算。
    我们其实不难想到,如果在遍历j 的时候,每个i 到j 的子串都要判断其是不是回文串,每个都暴力判断的话,必然是有大量冗余的。比如对原字符串:abbacbcabba, 在判断子串acbca是不是回文串的时候,cbc势必是之前已经判断过了,而且我们知道它是回文串,是不是只需要判断第一个字符和最后一个字符,逻辑与上cbc的结果,就能判断出来它是不是回文串了。
    因此要定义的新的数组b[][] ,是用来存储s.substr(i,j)是不是回文串的。数据类型是bool型。

问题引申:如何找到一个字符串中究竟有多少个回文子串?

这也是很容易想到的就是两层遍历,其实也可以用动态规划的思路来接,b[i][j]代表从i 到j 的子串是否是回文串,和刚才讲的是一模一样的,b[i][j] = (s[i]==s[j] && (j-i<2 || b[i+1]b[j-1]))。看起来好像很吓人,其实很简单,就是判断首尾是否相等,再去查去掉首尾的是不是回文串就行。
但是这里为了实现自底向上,遍历的方式要稍稍注意一下

for(int i = 0;i<s.length();i++){
	for (int j=0;j<=i;j++)}

或者

for(int i = s.length()-1;i>=0;i--){
	for (int j=i;j<s.length();j++)}

最好采用后面的一种,因为保证了j>i, 比较符合直观。
但是千万不能

for(int i = 0;i<s.length();i++){
	for (int j=i;j<s.length();j++)}

因为这样的遍历方法,会导致找不到相关的查询值,无法实现自底向上。我真是太聪明了。

代码

好了,大致的注意事项都说明了,这里还是言归正传,看看代码吧,简单得令人发指。

class Solution {
public:
    int minCut(string s) {
        if (s.length()<2)
            return 0;
        vector<int> dp(s.length()+1, INT_MAX);
        vector<vector<bool>> b(s.length(),vector<bool> (s.length()));
        dp[s.length()] = -1;
        for(int i= s.length()-1; i>=0;i--){
            for(int j=i;j<s.length();j++){
                if ((s[i]==s[j])&&(j-i<2||b[i+1][j-1])){
                    b[i][j] = true;
                    dp[i] = min(dp[i], dp[j+1]+1);
                }
                else
                    b[i][j] = false;
            }
        }
        return dp[0];
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章