国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++力扣題目131--分割回文串

這篇具有很好參考價(jià)值的文章主要介紹了C++力扣題目131--分割回文串。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

131. 分割回文串

給你一個(gè)字符串?s,請你將?s?分割成一些子串,使每個(gè)子串都是?回文串?。返回?s?所有可能的分割方案。

回文串?是正著讀和反著讀都一樣的字符串。

示例 1:

輸入:s = "aab"
輸出:[["a","a","b"],["aa","b"]]

示例 2:

輸入:s = "a"
輸出:[["a"]]

提示:

  • 1 <= s.length <= 16
  • s?僅由小寫英文字母組成

思路

本題這涉及到兩個(gè)關(guān)鍵問題:

  1. 切割問題,有不同的切割方式
  2. 判斷回文

相信這里不同的切割方式可以搞懵很多同學(xué)了。

這種題目,想用for循環(huán)暴力解法,可能都不那么容易寫出來,所以要換一種暴力的方式,就是回溯。

一些同學(xué)可能想不清楚 回溯究竟是如何切割字符串呢?

我們來分析一下切割,其實(shí)切割問題類似組合問題

例如對于字符串a(chǎn)bcdef:

  • 組合問題:選取一個(gè)a之后,在bcdef中再去選取第二個(gè),選取b之后在cdef中再選取第三個(gè).....。
  • 切割問題:切割一個(gè)a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。

感受出來了不?

所以切割問題,也可以抽象為一棵樹形結(jié)構(gòu),如圖:

C++力扣題目131--分割回文串,c++,leetcode,算法

遞歸用來縱向遍歷,for循環(huán)用來橫向遍歷,切割線(就是圖中的紅線)切割到字符串的結(jié)尾位置,說明找到了一個(gè)切割方法。

此時(shí)可以發(fā)現(xiàn),切割問題的回溯搜索的過程和組合問題的回溯搜索的過程是差不多的。

#回溯三部曲

  • 遞歸函數(shù)參數(shù)

全局變量數(shù)組path存放切割后回文的子串,二維數(shù)組result存放結(jié)果集。 (這兩個(gè)參數(shù)可以放到函數(shù)參數(shù)里)

本題遞歸函數(shù)參數(shù)還需要startIndex,因?yàn)榍懈钸^的地方,不能重復(fù)切割,和組合問題也是保持一致的。

在回溯算法:求組合總和(二)?(opens new window)中我們深入探討了組合問題什么時(shí)候需要startIndex,什么時(shí)候不需要startIndex。

代碼如下:

vector<vector<string>> result;
vector<string> path; // 放已經(jīng)回文的子串
void backtracking (const string& s, int startIndex) {
  • 遞歸函數(shù)終止條件

C++力扣題目131--分割回文串,c++,leetcode,算法

從樹形結(jié)構(gòu)的圖中可以看出:切割線切到了字符串最后面,說明找到了一種切割方法,此時(shí)就是本層遞歸的終止條件。

那么在代碼里什么是切割線呢?

在處理組合問題的時(shí)候,遞歸參數(shù)需要傳入startIndex,表示下一輪遞歸遍歷的起始位置,這個(gè)startIndex就是切割線。

所以終止條件代碼如下:

void backtracking (const string& s, int startIndex) {
    // 如果起始位置已經(jīng)大于s的大小,說明已經(jīng)找到了一組分割方案了
    if (startIndex >= s.size()) {
        result.push_back(path);
        return;
    }
}
  • 單層搜索的邏輯

來看看在遞歸循環(huán)中如何截取子串呢?

for (int i = startIndex; i < s.size(); i++)循環(huán)中,我們 定義了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。

首先判斷這個(gè)子串是不是回文,如果是回文,就加入在vector<string> path中,path用來記錄切割過的回文子串。

代碼如下:

for (int i = startIndex; i < s.size(); i++) {
    if (isPalindrome(s, startIndex, i)) { // 是回文子串
        // 獲取[startIndex,i]在s中的子串
        string str = s.substr(startIndex, i - startIndex + 1);
        path.push_back(str);
    } else {                // 如果不是則直接跳過
        continue;
    }
    backtracking(s, i + 1); // 尋找i+1為起始位置的子串
    path.pop_back();        // 回溯過程,彈出本次已經(jīng)添加的子串
}

注意切割過的位置,不能重復(fù)切割,所以,backtracking(s, i + 1); 傳入下一層的起始位置為i + 1。

#判斷回文子串

最后我們看一下回文子串要如何判斷了,判斷一個(gè)字符串是否是回文。

可以使用雙指針法,一個(gè)指針從前向后,一個(gè)指針從后向前,如果前后指針?biāo)赶虻脑厥窍嗟鹊模褪腔匚淖址恕?/p>

那么判斷回文的C++代碼如下:

 bool isPalindrome(const string& s, int start, int end) {
     for (int i = start, j = end; i < j; i++, j--) {
         if (s[i] != s[j]) {
             return false;
         }
     }
     return true;
 }

如果大家對雙指針法有生疏了,傳送門:雙指針法:總結(jié)篇!(opens new window)

此時(shí)關(guān)鍵代碼已經(jīng)講解完畢,整體代碼如下(詳細(xì)注釋了)

根據(jù)Carl給出的回溯算法模板:

void backtracking(參數(shù)) {
    if (終止條件) {
        存放結(jié)果;
        return;
    }

    for (選擇:本層集合中元素(樹中節(jié)點(diǎn)孩子的數(shù)量就是集合的大?。? {
        處理節(jié)點(diǎn);
        backtracking(路徑,選擇列表); // 遞歸
        回溯,撤銷處理結(jié)果
    }
}

不難寫出如下代碼:

class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已經(jīng)回文的子串
    void backtracking (const string& s, int startIndex) {
        // 如果起始位置已經(jīng)大于s的大小,說明已經(jīng)找到了一組分割方案了
        if (startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome(s, startIndex, i)) {   // 是回文子串
                // 獲取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳過
                continue;
            }
            backtracking(s, i + 1); // 尋找i+1為起始位置的子串
            path.pop_back(); // 回溯過程,彈出本次已經(jīng)添加的子串
        }
    }
    bool isPalindrome(const string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }
public:
    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();
        backtracking(s, 0);
        return result;
    }
};


?

  • 時(shí)間復(fù)雜度: O(n * 2^n)
  • 空間復(fù)雜度: O(n^2)

#優(yōu)化

上面的代碼還存在一定的優(yōu)化空間, 在于如何更高效的計(jì)算一個(gè)子字符串是否是回文字串。上述代碼isPalindrome函數(shù)運(yùn)用雙指針的方法來判定對于一個(gè)字符串s, 給定起始下標(biāo)和終止下標(biāo), 截取出的子字符串是否是回文字串。但是其中有一定的重復(fù)計(jì)算存在:

例如給定字符串"abcde", 在已知"bcd"不是回文字串時(shí), 不再需要去雙指針操作"abcde"而可以直接判定它一定不是回文字串。

具體來說, 給定一個(gè)字符串s, 長度為n, 它成為回文字串的充分必要條件是s[0] == s[n-1]s[1:n-1]是回文字串。

大家如果熟悉動(dòng)態(tài)規(guī)劃這種算法的話, 我們可以高效地事先一次性計(jì)算出, 針對一個(gè)字符串s, 它的任何子串是否是回文字串, 然后在我們的回溯函數(shù)中直接查詢即可, 省去了雙指針移動(dòng)判定這一步驟.

具體參考代碼如下:

class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已經(jīng)回文的子串
    vector<vector<bool>> isPalindrome; // 放事先計(jì)算好的是否回文子串的結(jié)果
    void backtracking (const string& s, int startIndex) {
        // 如果起始位置已經(jīng)大于s的大小,說明已經(jīng)找到了一組分割方案了
        if (startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome[startIndex][i]) {   // 是回文子串
                // 獲取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳過
                continue;
            }
            backtracking(s, i + 1); // 尋找i+1為起始位置的子串
            path.pop_back(); // 回溯過程,彈出本次已經(jīng)添加的子串
        }
    }
    void computePalindrome(const string& s) {
        // isPalindrome[i][j] 代表 s[i:j](雙邊包括)是否是回文字串 
        isPalindrome.resize(s.size(), vector<bool>(s.size(), false)); // 根據(jù)字符串s, 刷新布爾矩陣的大小
        for (int i = s.size() - 1; i >= 0; i--) { 
            // 需要倒序計(jì)算, 保證在i行時(shí), i+1行已經(jīng)計(jì)算好了
            for (int j = i; j < s.size(); j++) {
                if (j == i) {isPalindrome[i][j] = true;}
                else if (j - i == 1) {isPalindrome[i][j] = (s[i] == s[j]);}
                else {isPalindrome[i][j] = (s[i] == s[j] && isPalindrome[i+1][j-1]);}
            }
        }
    }
public:
    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();
        computePalindrome(s);
        backtracking(s, 0);
        return result;
    }
};



?

#總結(jié)

這道題目在leetcode上是中等,但可以說是hard的題目了,但是代碼其實(shí)就是按照模板的樣子來的。

那么難究竟難在什么地方呢?

我列出如下幾個(gè)難點(diǎn):

  • 切割問題可以抽象為組合問題
  • 如何模擬那些切割線
  • 切割問題中遞歸如何終止
  • 在遞歸循環(huán)中如何截取子串
  • 如何判斷回文

我們平時(shí)在做難題的時(shí)候,總結(jié)出來難究竟難在哪里也是一種需要鍛煉的能力。

一些同學(xué)可能遇到題目比較難,但是不知道題目難在哪里,反正就是很難。其實(shí)這樣還是思維不夠清晰,這種總結(jié)的能力需要多接觸多鍛煉。

本題我相信很多同學(xué)主要卡在了第一個(gè)難點(diǎn)上:就是不知道如何切割,甚至知道要用回溯法,也不知道如何用。也就是沒有體會到按照求組合問題的套路就可以解決切割

如果意識到這一點(diǎn),算是重大突破了。接下來就可以對著模板照葫蘆畫瓢。

但接下來如何模擬切割線,如何終止,如何截取子串,其實(shí)都不好想,最后判斷回文算是最簡單的了。

關(guān)于模擬切割線,其實(shí)就是index是上一層已經(jīng)確定了的分割線,i是這一層試圖尋找的新分割線

除了這些難點(diǎn),本題還有細(xì)節(jié),例如:切割過的地方不能重復(fù)切割所以遞歸函數(shù)需要傳入i + 1。

所以本題應(yīng)該是一道hard題目了。

可能刷過這道題目的錄友都沒感受到自己原來克服了這么多難點(diǎn),就把這道題目AC了,這應(yīng)該叫做無招勝有招,人碼合一。文章來源地址http://www.zghlxwxcb.cn/news/detail-809252.html

到了這里,關(guān)于C++力扣題目131--分割回文串的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Leetcode】 131. 分割回文串

    【Leetcode】 131. 分割回文串

    給你一個(gè)字符串 s ,請你將 s 分割成一些子串,使每個(gè)子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正著讀和反著讀都一樣的字符串。 示例 1: 輸入 : s = \\\"aab\\\" 輸出 : [[\\\"a\\\",\\\"a\\\",\\\"b\\\"],[\\\"aa\\\",\\\"b\\\"]] 示例 2 : 輸入 : s = \\\"a\\\" 輸出 : [[\\\"a\\\"]] 提示: s 僅由小寫英文字母組成

    2024年02月07日
    瀏覽(21)
  • 【刷題1】LeetCode 131. 分割回文串 java題解

    2024: 剛開始做leetcode hot100,查閱自己以前寫的題解專欄,發(fā)現(xiàn)沒有這一題,于是加上??赡躭eetcode100更新了吧。我看現(xiàn)在leetcode100官網(wǎng)的題目已經(jīng)是分好類的了,以前我的題解帖子是自己手動(dòng)分類整理的。

    2024年02月19日
    瀏覽(19)
  • 【leetcode 力扣刷題】回文串相關(guān)題目(KMP、動(dòng)態(tài)規(guī)劃)

    【leetcode 力扣刷題】回文串相關(guān)題目(KMP、動(dòng)態(tài)規(guī)劃)

    題目鏈接:5. 最長回文子串 題目內(nèi)容: 題目就是要我們找s中的回文子串,還要是最長的。其實(shí)想想,暴力求解也行……就是遍歷所有的子串,同時(shí)判斷是不是回文串,是的話再和記錄的最大長度maxlen比較,如果更長就更新。時(shí)間復(fù)雜度直接變成O(n^3)。 優(yōu)化的點(diǎn)在于,假設(shè)子

    2024年02月09日
    瀏覽(27)
  • day27 | 39. 組合總和、 40.組合總和II、131.分割回文串

    目錄: 39.?組合總和 給你一個(gè)? 無重復(fù)元素 ?的整數(shù)數(shù)組? candidates ?和一個(gè)目標(biāo)整數(shù)? target ?,找出? candidates ?中可以使數(shù)字和為目標(biāo)數(shù)? target ?的 所有?** 不同組合 ?,并以列表形式返回。你可以按? 任意順序 ?返回這些組合。 candidates ?中的? 同一個(gè) ?數(shù)字可以? 無限

    2024年02月10日
    瀏覽(17)
  • (動(dòng)態(tài)規(guī)劃) 132. 分割回文串 II ——【Leetcode每日一題】

    (動(dòng)態(tài)規(guī)劃) 132. 分割回文串 II ——【Leetcode每日一題】

    難度:困難 給你一個(gè)字符串 s ,請你將 s 分割成一些子串,使每個(gè)子串都是回文。 返回符合要求的 最少分割次數(shù) 。 示例 1: 輸入:s = “aab” 輸出:1 解釋:只需一次分割就可將 s 分割成 [“aa”,“b”] 這樣兩個(gè)回文子串。 示例 2: 輸入:s = “a” 輸出:0 示例 3: 輸入:

    2024年02月15日
    瀏覽(43)
  • C++力扣題目77--組合

    C++力扣題目77--組合

    給定兩個(gè)整數(shù)? n ?和? k ,返回范圍? [1, n] ?中所有可能的? k ?個(gè)數(shù)的組合。 你可以按? 任何順序 ?返回答案。 示例 1: 示例 2: 提示: 1 = n = 20 1 = k = n 本題是回溯法的經(jīng)典題目。 直接的解法當(dāng)然是使用for循環(huán),例如示例中k為2,很容易想到 用兩個(gè)for循環(huán),這樣就可以輸

    2024年01月17日
    瀏覽(18)
  • C++力扣題目37--解數(shù)獨(dú)

    C++力扣題目37--解數(shù)獨(dú)

    力扣題目鏈接(opens new window) 編寫一個(gè)程序,通過填充空格來解決數(shù)獨(dú)問題。 一個(gè)數(shù)獨(dú)的解法需遵循如下規(guī)則: 數(shù)字 1-9 在每一行只能出現(xiàn)一次。 數(shù)字 1-9 在每一列只能出現(xiàn)一次。 數(shù)字 1-9 在每一個(gè)以粗實(shí)線分隔的 3x3 宮內(nèi)只能出現(xiàn)一次。 空白格用 \\\'.\\\' 表示。 一個(gè)數(shù)獨(dú)。 答案

    2024年01月21日
    瀏覽(17)
  • C++力扣題目39--組合總和

    C++力扣題目39--組合總和

    給你一個(gè)? 無重復(fù)元素 ?的整數(shù)數(shù)組? candidates ?和一個(gè)目標(biāo)整數(shù)? target ?,找出? candidates ?中可以使數(shù)字和為目標(biāo)數(shù)? target ?的 所有 ? 不同組合 ?,并以列表形式返回。你可以按? 任意順序 ?返回這些組合。 candidates ?中的? 同一個(gè) ?數(shù)字可以? 無限制重復(fù)被選取 ?。如果

    2024年01月17日
    瀏覽(21)
  • 算法刷題Day 27 組合總和+組合總和II+分割回文子串

    本題的特點(diǎn)是元素為可重復(fù)選取的 其實(shí)只要在原來的基礎(chǔ)上添加一點(diǎn)小小的變化就是實(shí)現(xiàn)重復(fù)選取(與排列區(qū)別開),一時(shí)沒想出來 這里與39.組合總和 (opens new window)最大的不同就是要去重了。 前面我們提到:要去重的是“同一樹層上的使用過”,如何判斷同一樹層上元素(

    2024年02月14日
    瀏覽(20)
  • Leetcode算法系列| 9. 回文數(shù)

    Leetcode算法系列| 9. 回文數(shù)

    給你一個(gè)整數(shù) x ,如果 x 是一個(gè)回文整數(shù),返回 true ;否則,返回 false 。 回文數(shù)是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數(shù)。 例如,121 是回文,而 123 不是。 示例1: 示例 2: 示例3: 提示: 2^31 = x = 2^31 - 1 映入腦海的第一個(gè)想法是將數(shù)字轉(zhuǎn)換為字符串,并

    2024年02月03日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包