diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index a54d6576..37132503 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -206,6 +206,65 @@ public: return result; } }; +``` +# 优化 + +上面的代码还存在一定的优化空间, 在于如何更高效的计算一个子字符串是否是回文字串。上述代码```isPalindrome```函数运用双指针的方法来判定对于一个字符串```s```, 给定起始下标和终止下标, 截取出的子字符串是否是回文字串。但是其中有一定的重复计算存在: + +例如给定字符串```"abcde"```, 在已知```"bcd"```不是回文字串时, 不再需要去双指针操作```"abcde"```而可以直接判定它一定不是回文字串。 + +具体来说, 给定一个字符串`s`, 长度为```n```, 它成为回文字串的充分必要条件是```s[0] == s[n-1]```且```s[1:n-1]```是回文字串。 + +大家如果熟悉动态规划这种算法的话, 我们可以高效地事先一次性计算出, 针对一个字符串```s```, 它的任何子串是否是回文字串, 然后在我们的回溯函数中直接查询即可, 省去了双指针移动判定这一步骤. + +具体参考代码如下: + +```CPP +class Solution { +private: + vector> result; + vector path; // 放已经回文的子串 + vector> isPalindrome; // 放事先计算好的是否回文子串的结果 + void backtracking (const string& s, int startIndex) { + // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了 + 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(); // 回溯过程,弹出本次已经填在的子串 + } + } + void computePalindrome(const string& s) { + // isPalindrome[i][j] 代表 s[i:j](双边包括)是否是回文字串 + isPalindrome.resize(s.size(), vector(s.size(), false)); // 根据字符串s, 刷新布尔矩阵的大小 + for (int i = s.size() - 1; i >= 0; i--) { + // 需要倒序计算, 保证在i行时, i+1行已经计算好了 + 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> partition(string s) { + result.clear(); + path.clear(); + computePalindrome(s); + backtracking(s, 0); + return result; + } +}; + ``` # 总结