LeetCode 131 分割回文串 - 回溯解法

LeetCode 131 分割回文串 - 回溯解法

https://leetcode.cn/problems/palindrome-partitioning/description/

思路

手动模拟

s = “aab”

先切为 [a, ab]:左边是回文串;右边继续切 "ab",可切为 [a, b],也都是回文串。

先切为 [aa, b]:左边是回文串,右边也是回文串。

每次都先切出左侧一段,再递归处理右侧。

对于 “aaba”

[a, aba] 这种情况不会遗漏,因为切分位置可以放到最右侧,此时右侧为空串。

为什么不一开始在最左侧切,让右侧保持完整 aba?因为如果允许左侧为空,path 的处理要额外加判断逻辑。

错误代码

for(从每个位置切){
	if 左边是回文串{
		path.push(左边)
		qie(右边)
		path.pop()
	}
}
这里还没有返回
返回的逻辑
if(s.size()==1){

            path.push_back(s);

            result.push_back(path);

            return;

        }
这里是错误的:在返回分支中执行了 `path.push`,但没有对应的 `pop`,会导致 `path` 残留脏数据。

path.push(左边)
qie(右边)
path.pop()

按理来说这里 `pop` 的应该是左边片段,但由于前面的返回分支额外执行了 `push`,这里 `pop` 的反而是刚才 `return` 分支压入的 `s`,会导致结果错误。
对于 "aab"
输出是 [["a","a","b"],["a","aa","b"]]
可以看到最里面的 `a` 没有被正确弹出。
解决方式:不要在返回分支执行 `path`  `push/pop`。因此终止条件改为“字符串为空时保存结果并返回”,而不是“长度为 1 时处理”。
class Solution {

public:

    vector<vector<string>> result;

    vector<string> path;

    bool is_huiwen(string s){

        for(int i = 0, j = s.size()-1;i<=j;i++,j--){

            if(s[i]!=s[j])return false;

        }

        return true;

    }

    void qie(string s){

        if(s.size()==1){

            path.push_back(s);

            result.push_back(path);

            return;

        }

        for(int i = 1;i<=s.size();i++){

            string left = s.substr(0,i);

            string right = s.substr(i,s.size()-i);

            if(is_huiwen(left)){

                path.push_back(left);

                qie(right);

                path.pop_back();

            }

        }

    }

    vector<vector<string>> partition(string s) {

        qie(s);

        return result;

    }

};

正确代码

class Solution {

public:

    vector<vector<string>> result;

    vector<string> path;

    bool is_huiwen(string s){

        for(int i = 0, j = s.size()-1;i<=j;i++,j--){

            if(s[i]!=s[j])return false;

        }

        return true;

    }

    void qie(string s){
		//遍历完所有情况, 右侧为空, 传入s为空, 此时返回
        if(s.size()==0){

            // path.push_back(s);

            result.push_back(path);

            return;

        }

        for(int i = 1;i<=s.size();i++){

            string left = s.substr(0,i);

            string right = s.substr(i,s.size()-i);

            if(is_huiwen(left)){

                path.push_back(left);

                qie(right);

                path.pop_back();

            }

        }

    }

    vector<vector<string>> partition(string s) {

        qie(s);

        return result;

    }

};

理解

做完后的理解, 一开始这个问题一直没有找到一个不变的流程,但后面想到了:

从串的前面(遍历式)截下来一个回文串并加到path中,不断地重复直到串为空.

仍然是往path中加值.

这个不变的流程是 函数的语义: 从串的前面不断切出回文串

if(s为空){
	result保存, 返回
}
for(遍历切割位置){
	切下来的左边为回文串{
		左边加入到path
		qie(右边)
		path pop
	}
}