131. 分割回文串
题目描述:
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
解答:
其实刚看到懵了一下,切割问题怎么给转化为组合问题的?该怎么去回溯?
如何转化,如下图所示:

在组合问题中,每次是选择一个,到这个切割问题里,树的深度其实就是按照次序不同的子串,树的宽度是不同长度的字串。
(1)那有个问题,这种切割线应该如何来表达呢?
切割线每次都往下推一个 ,在下一个开始元素之前,所以使用i+1表示即可。
(2)第二个问题,切割问题里递归应该如何终止?
当切割线到最后一个元素后面时即可终止
(3)第三个问题,递归时怎么切割子串?
在for (int i = startIndex; i < s.size(); i++)循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。
(4)如何判断回文?
双指针法,一前一后即可。
代码实现:
- class Solution {
- private:
- vector
> result; - vector
path; // 放已经回文的子串 - 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(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;
- }
- public:
- vector
> partition(string s) { - result.clear();
- path.clear();
- backtracking(s, 0);
- return result;
- }
- };
93. 复原 IP 地址
题目描述:
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
解答:
这个和上一道题切割回文串其实很类似。这道题需要对一串数字进行切割并且加入.
这道题回溯时树的宽度采用和上一题类似的情况,深度就是每次从下一个位置开始寻找
那么如何终止?
IP地址有三个点,那检测到串中有了三个点,就可以终止了;需要额外注意的是,此时还得去判断一下第四段IP是否满足要求。
多加的一个函数就是一个IP合法性判断函数,简单来说就是数字要合法、范围要合法。
代码实现:
- class Solution {
- private:
- vector
result;// 记录结果 - // startIndex: 搜索的起始位置,pointNum:添加逗点的数量
- void backtracking(string& s, int startIndex, int pointNum) {
- if (pointNum == 3) { // 逗点数量为3时,分隔结束
- // 判断第四段子字符串是否合法,如果合法就放进result中
- 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)) { // 判断 [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; // 不合法,直接结束本层循环
- }
- }
- // 判断字符串s在左闭又闭区间[start, end]所组成的数字是否合法
- bool isValid(const string& s, int start, int end) {
- if (start > end) {
- return false;
- }
- if (s[start] == '0' && start != end) { // 0开头的数字不合法
- return false;
- }
- 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) { // 如果大于255了不合法
- return false;
- }
- }
- return true;
- }
- public:
- vector
restoreIpAddresses(string s) { - result.clear();
- if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了
- backtracking(s, 0, 0);
- return result;
- }
- };