成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

分割回文串,有點難!

開發 前端
遞歸用來縱向遍歷,for循環用來橫向遍歷,切割線(就是圖中的紅線)切割到字符串的結尾位置,說明找到了一個切割方法。

[[426079]]

切割問題其實是一種組合問題!

分割回文串

力扣題目鏈接:https://leetcode-cn.com/problems/palindrome-partitioning/

給定一個字符串 s,將 s 分割成一些子串,使每個子串都是回文串。

返回 s 所有可能的分割方案。

示例:

輸入: "aab"

輸出: [ ["aa","b"], ["a","a","b"] ]

思路

本題這涉及到兩個關鍵問題:

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

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

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

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

我們來分析一下切割,其實切割問題類似組合問題。

例如對于字符串abcdef:

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

感受出來了不?

所以切割問題,也可以抽象為一顆樹形結構,如圖:

分割回文串

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

此時可以發現,切割問題的回溯搜索的過程和組合問題的回溯搜索的過程是差不多的。

回溯三部曲

  • 遞歸函數參數

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

本題遞歸函數參數還需要startIndex,因為切割過的地方,不能重復切割,和組合問題也是保持一致的。

在39. 組合總和中我們深入探討了組合問題什么時候需要startIndex,什么時候不需要startIndex。

代碼如下:

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

分割回文串

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

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

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

所以終止條件代碼如下:

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

來看看在遞歸循環,中如何截取子串呢?

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

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

代碼如下:

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

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

判斷回文子串

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

可以使用雙指針法,一個指針從前向后,一個指針從后先前,如果前后指針所指向的元素是相等的,就是回文字符串了。

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

  1. bool isPalindrome(const string& s, int start, int end) { 
  2.     for (int i = start, j = end; i < j; i++, j--) { 
  3.         if (s[i] != s[j]) { 
  4.             return false
  5.         } 
  6.     } 
  7.     return true

如果大家對雙指針法有生疏了,傳送門:雙指針法:總結篇!

此時關鍵代碼已經講解完畢,整體代碼如下(詳細注釋了)

C++整體代碼

根據Carl給出的回溯算法模板:

  1. void backtracking(參數) { 
  2.     if (終止條件) { 
  3.         存放結果; 
  4.         return
  5.     } 
  6.  
  7.     for (選擇:本層集合中元素(樹中節點孩子的數量就是集合的大小)) { 
  8.         處理節點; 
  9.         backtracking(路徑,選擇列表); // 遞歸 
  10.         回溯,撤銷處理結果 
  11.     } 

不難寫出如下代碼:

  1. class Solution { 
  2. private: 
  3.     vector<vector<string>> result; 
  4.     vector<string> path; // 放已經回文的子串 
  5.     void backtracking (const string& s, int startIndex) { 
  6.         // 如果起始位置已經大于s的大小,說明已經找到了一組分割方案了 
  7.         if (startIndex >= s.size()) { 
  8.             result.push_back(path); 
  9.             return
  10.         } 
  11.         for (int i = startIndex; i < s.size(); i++) { 
  12.             if (isPalindrome(s, startIndex, i)) {   // 是回文子串 
  13.                 // 獲取[startIndex,i]在s中的子串 
  14.                 string str = s.substr(startIndex, i - startIndex + 1); 
  15.                 path.push_back(str); 
  16.             } else {                                // 不是回文,跳過 
  17.                 continue
  18.             } 
  19.             backtracking(s, i + 1); // 尋找i+1為起始位置的子串 
  20.             path.pop_back(); // 回溯過程,彈出本次已經填在的子串 
  21.         } 
  22.     } 
  23.     bool isPalindrome(const string& s, int start, int end) { 
  24.         for (int i = start, j = end; i < j; i++, j--) { 
  25.             if (s[i] != s[j]) { 
  26.                 return false
  27.             } 
  28.         } 
  29.         return true
  30.     } 
  31. public
  32.     vector<vector<string>> partition(string s) { 
  33.         result.clear(); 
  34.         path.clear(); 
  35.         backtracking(s, 0); 
  36.         return result; 
  37.     } 
  38. }; 

總結

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

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

我列出如下幾個難點:

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

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

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

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

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

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

關于模擬切割線,其實就是index是上一層已經確定了的分割線,i是這一層試圖尋找的新分割線

除了這些難點,本題還有細節,例如:切割過的地方不能重復切割所以遞歸函數需要傳入i + 1。

所以本題應該是一個道hard題目了。

可能刷過這道題目的錄友都沒感受到自己原來克服了這么多難點,就把這道題目AC了,這應該叫做無招勝有招,人碼合一,哈哈哈。

本文轉載自微信公眾號「代碼隨想錄」,可以通過以下二維碼關注。轉載本文請聯系代碼隨想錄公眾號。

 

責任編輯:武曉燕 來源: 代碼隨想錄
相關推薦

2021-11-12 09:44:03

字符串算法復雜度

2021-06-07 05:32:53

Webpack Chunk 前端

2014-04-03 09:36:22

iPad版Office高效辦公

2019-12-18 16:02:24

AI人工智能

2020-02-07 17:14:44

手機小米手機行業

2009-08-07 14:15:21

C#字符串分割

2010-11-26 10:43:48

MySQL分割字符串

2010-11-26 13:27:41

MySQL存儲過程

2021-09-01 07:59:44

HTTPweb瀏覽器

2023-07-07 08:24:07

css顏色變量

2009-12-01 09:18:50

PHP分割字符串

2020-11-03 18:36:37

面試字符串算法

2010-10-09 11:43:10

MYSQL字符串

2022-12-06 08:27:50

Bash腳本字符串

2021-09-07 06:40:25

貪心平衡字符串

2023-02-26 00:00:02

字符串分割String

2021-03-08 08:23:24

Java字符串截取

2020-02-04 18:06:28

千年一遇對稱日回文算法

2023-12-15 09:49:54

回溯解決組合問題數組

2021-10-09 07:10:32

遞增子序列回溯法
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本精品视频在线观看 | 久久精品色欧美aⅴ一区二区 | 五月婷六月丁香 | 97视频免费 | 国产97在线看 | 国产伦精品一区二区 | 久久久久国产一级毛片 | 五月天国产 | 久久久毛片 | 中文字幕一区二区三区精彩视频 | 久热国产精品 | 久久国产亚洲精品 | 91精品国产综合久久婷婷香蕉 | 色婷婷综合网 | 亚洲欧美日韩一区 | 国产伦精品一区二区三毛 | 亚洲第一成年免费网站 | 国产成人精品a视频一区www | 国产精品二区三区 | 欧美成人精品在线 | 成年人黄色一级片 | 国产二区三区 | 成人二区 | 自拍视频在线观看 | 亚洲国产一区二区在线 | 国产9 9在线 | 中文 | 成人免费看电影 | 国产高清精品在线 | 日韩国产在线观看 | av网站在线播放 | 精品久久99 | 国产黄色电影 | 国产欧美在线 | 欧美一级片在线看 | 日韩中文一区二区三区 | 狠狠干狠狠插 | 99久久婷婷国产综合精品首页 | 最新日韩av | 三级黄片毛片 | 欧美 日韩 国产 成人 在线 | 午夜精品一区二区三区在线观看 |