分割回文串
根據代碼隨想錄的題解總結
思路
本題涉及到兩個關鍵問題:
1、如何切割
2、判斷是否回文
「其實切割問題類似組合問題」。所以我們要利用回溯法來切割字符串。
例如對于字符串abcdef:
組合問題:選取一個a之后,在bcdef中再去選取第二個,選取b之后在cdef中在選取第三個…。
切割問題:切割一個a之后,在bcdef中再去切割第二段,切割b之后在cdef中在切割第三段…。
所以切割問題,也可以抽象為一顆樹形結構,如圖:
遞歸用來縱向遍歷,for循環用來橫向遍歷,切割線(就是圖中的紅線)切割到字符串的結尾位置,說明找到了一個切割方法。
此時可以發現,切割問題的回溯搜索的過程和組合問題的回溯搜索的過程是差不多的
切割字符串
回溯三部曲
遞歸函數參數
全局變量數組path存放切割后回文的子串,二維數組result存放結果集。
還需要startIndex,因為切割過的地方,不能重復切割,和組合問題也是保持一致的。
遞歸函數終止條件
由上圖看出,切割線startIndex切到了字符串的最后面,說明找到了一種切割方法,此時就是本層遞歸的終止終止條件。
單層搜索的邏輯
在for (int i = startIndex; i < s.size(); i++)循環中,我們 定義了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。
首先判斷這個子串是不是回文,如果是回文,就加入在vector 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(); // 回溯過程,彈出本次已經填在的子串 }判斷回文子串
判斷一個字符串是否是回文。
可以使用雙指針法,一個指針從前向后,一個指針從后先前,如果前后指針所指向的元素是相等的,就是回文字符串了。
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;} class Solution { public:bool isPalindrome(int start,int end,string &s){for(;start<end;start++,end--){if(s[start]!=s[end]) return false;}return true;}vector<vector<string>>res;vector<string> path; // 放已經回文的子串void backtracking(string &s,int startIndex){if(startIndex>=s.size()){ // 如果起始位置已經大于s的大小,說明已經找到了一組分割方案了res.push_back(path);return;}for(int ii=startIndex;ii<s.size();ii++){string s1=s.substr(startIndex,ii-startIndex+1);// 獲取[startIndex,i]在s中的子串if(isPalindrome(0,s1.size()-1,s1)==true){// 是回文子串path.push_back(s1);}else continue; // 不是回文,跳過backtracking(s,ii+1);// 尋找i+1為起始位置的子串path.pop_back();}}vector<vector<string>> partition(string s) {backtracking(s,0);return res;} };本題難點:
切割問題可以抽象為組合問題
如何模擬那些切割線
切割問題中遞歸如何終止
在遞歸循環中如何截取子串
如何判斷回文
總結
- 上一篇: centos7 下安装mysql5.7
- 下一篇: 复原ip地址