131. 分割回文串
分割问题可以看作一种特殊的组合问题
如图,分割”aab”,横向选择不同的分割位置,纵向在余下的部分继续分割(递归的思想:分割字符串、子问题为分割子字符串)
回溯三部曲:
- 参数
参数是要分割的字符串、子串的起始位置
void backtracking(const string& s, int startIndex) {
- 终止条件
如果切割线startIndex达到了字符串的末尾,那么分割完成,保存结果并返回
if (startIndex >= s.size()) {
result.push_back(path);
return;
}
- 单层搜索的逻辑
在for (int i = startIndex; i < s.size(); i++)
循环中,我们 定义了起始位置startIndex
,那么 [startIndex, i]
就是要截取的子串。
首先判断这个子串是不是回文,如果是回文,就加入在vector<string> path
中,path用来记录切割过的回文子串。
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome(s, startIndex, i)) {
path.push_back(s.substr(startIndex, i - startIndex + 1));
} else {
continue;
}
backtracking(s, i + 1);
path.pop_back();
}
完整代码:
class Solution {
public:
vector<vector<string>> result;
vector<string> path;
// 双指针法判断是否为回文字符串
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;
}
void backtracking(const string& s, int startIndex) {
if (startIndex >= s.size()) {
result.push_back(path);
return;
}
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome(s, startIndex, i)) {
path.push_back(s.substr(startIndex, i - startIndex + 1));
} else {
continue;
}
backtracking(s, i + 1);
path.pop_back();
}
}
vector<vector<string>> partition(string s) {
backtracking(s, 0);
return result;
}
};
93. 复原 IP 地址
这还是一个分割问题,可以逐步分割,然后判断截取字段是否合法
用pointNum记录逗点数量,如果有3个,那么就已经分割为4段了
if (pointNum == 3) { // 逗点数量为3时,分隔结束
// 判断第四段子字符串是否合法,如果合法就放进result中
if (isValid(s, startIndex, s.size() - 1)) {
result.push_back(s);
}
return;
}
- 单层搜索的逻辑
与分割回文串那一题一样,for循环用于横向切割,然后递归处理剩下的部分
判断本层已经切割的部分是否合法,如果合法就保存结果,不合法就结束本层循环
然后就是递归和回溯的过程:
递归调用时,下一层递归的startIndex要从i+2开始(因为需要在字符串中加入了分隔符.),同时记录分割符的数量pointNum 要 +1。
回溯的时候,就将刚刚加入的分隔符. 删掉就可以了,pointNum也要-1。
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点
pointNum++;
backtracking(s, i + 2, pointNum); // 插入逗点之后下一个子串的起始位置为i+2
pointNum--; // 回溯
s.erase(s.begin() + i + 1); // 回溯删掉逗点
} else break; // 不合法,直接结束本层循环
}
判断数字是否合法:
bool isvalid(const string& s, int start, int end) {
if (start > end) return false;
// 以0开头的
if (s[start] == '0' && start != end) return false;
// 处理非数字和数值大于255的情况
int num = 0;
for (int i = start; i <= end; i++) {
if (s[i] > '9' || s[i] < '0') return fasle;
num = num * 10 + (s[i] - '0');
if (num > 255) return false
}
return true;
}
完整代码:
class Solution {
public:
vector<string> result;
void backtracking(string& s, int startIndex, int pointNum) {
// 终止条件
if (pointNum == 3) {
if (isValid(s, startIndex, s.size() - 1)) { // 第四段也合法,保存结果
result.push_back(s);
}
return;
}
// 单层搜索的逻辑
for (int i = startIndex; i < s.size(); i++) {
if (isValid(s, startIndex, i)) {
s.insert(s.begin() + i + 1, '.'); // 在分割的位置添加一个 .
backtracking(s, i + 2, pointNum + 1); // pointNum 是形参,隐含回溯
s.erase(s.begin() + i + 1); // 回溯, 把.删了
} else break;
}
}
bool isValid(const string& s, int start, int end) {
if (start > end) return false;
// 以0开头的
if (s[start] == '0' && start != end) return false;
// 处理非数字和数值大于255的情况
int num = 0;
for (int i = start; i <= end; i++) {
if (s[i] > '9' || s[i] < '0')
return false;
num = num * 10 + (s[i] - '0');
if (num > 255)
return false;
}
return true;
}
vector<string> restoreIpAddresses(string s) {
backtracking(s, 0, 0);
return result;
}
};