LeetCode 131. 分割回文串
📌 题目描述
题目级别:中等
给你一个字符串 s,请你将 s 分割成一些 子串 ,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
-
示例 1:
输入:
s = "aab"输出:
[["a","a","b"],["aa","b"]] -
示例 2:
输入:
s = "a"输出:
[["a"]]
💡 破题思路:DFS 模拟切割线
这道题是典型的组合/分割 问题,非常适合使用回溯算法(DFS)。
我们可以把字符串想象成一条长长的蛋糕,我们要决定在哪里"下刀"。
- 递归参数
u:代表我们当前准备切蛋糕的起始位置。 - 横向遍历
i:代表我们这一刀切在哪。子串s[u...i]就是我们切下来的这一块。 - 合法性检验与纵向递归 :如果我们切下来的这一块是回文串,我们就把它装进当前的路径篮子
path里,然后拿着剩下部分的起始位置i + 1,继续向深处递归。 - 回溯 :当这一条路探索完退回来时,我们需要把刚刚装进篮子的那一块拿出来(
path.pop_back()),去尝试其他的切法。
(⚠️ 细节提醒:在 C++ 中传递字符串时,务必使用 const string& s,避免每一次递归都发生大规模的内存拷贝,这是大厂面试的红线!)
💻 C++ 代码实现 (基础双指针判断)
cpp
class Solution {
public:
vector<vector<string>> res;
vector<string> path;
vector<vector<string>> partition(string s) {
dfs(s, 0);
return res;
}
// 优化点:使用 const string& 避免大量字符串拷贝耗时
void dfs(const string& s, int u)
{
// 终止条件:切割线已经到达了字符串的末尾,说明找到了一种完全切分方案
if (u == s.size())
{
res.push_back(path);
return ;
}
// i 代表这一刀切下去的位置,切出的子串为 s[u...i]
for (int i = u; i < s.size(); i ++ )
{
// 如果切出来的这一块是回文串,才允许继续往下切
if (isPart(s, u, i))
{
path.push_back(s.substr(u, i - u + 1)); // 处理节点:将回文子串加入路径
dfs(s, i + 1); // 向下递归:从下一截开始继续切
path.pop_back(); // 回溯:撤销选择,尝试下一刀切得更长一点
}
}
}
// 双指针法:判断 s[l...r] 是否为回文串
bool isPart(const string& s, int l, int r)
{
while (l < r)
{
if (s[l ++ ] != s[r -- ]) return false;
}
return true;
}
};