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];
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章