This commit is contained in:
parent
a145bbc05f
commit
19212e880b
14
include/s0131_palindrome_partitioning.hpp
Normal file
14
include/s0131_palindrome_partitioning.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef S0131_PALINDROME_PARTITIONING_HPP
|
||||
#define S0131_PALINDROME_PARTITIONING_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class S0131 {
|
||||
public:
|
||||
vector<vector<string>> partition(string s);
|
||||
};
|
||||
|
||||
#endif
|
@ -42,6 +42,7 @@
|
||||
|
||||
- [总结](./backtrack.md)
|
||||
- [组合问题](./combinations.md)
|
||||
- [切割问题](./split.md)
|
||||
|
||||
# STL
|
||||
|
||||
|
@ -9,10 +9,43 @@
|
||||
|
||||
关键就是要学会分析和画图,然后确定传入参数。只要传入参数确定了代码框架就确定了 (返回值一般是 `void`):
|
||||
|
||||
- 得有一个 `&result` 来存放结果
|
||||
- 每一层的节点所包含的数据是什么,需要一系列的参数来描述当前节点的状态(传入的参数实际上是上一轮迭代的数据)。
|
||||
- 终止条件是什么?回溯法中的每个节点并不是真的树状节点,没有 `nullptr` ,因此用空指针来判断是否到了叶子结点并不合理,需要其它的一些方法来确定是否到达叶子节点,比如高度。
|
||||
- 每次迭代要在上一次迭代的基础上进行哪些操作?需要哪些参数才能完成这些操作?
|
||||
- 思考这棵树怎么画,每层遍历的逻辑是什么,每条边的操作逻辑是什么。
|
||||
- 得设计一个数据结构 `NodeState` 来存放当前节点状态。该数据结构的可扩展性必须要强,需要满足以下条件:
|
||||
- 能描述当前节点的状态
|
||||
- 能作为最终结果存储
|
||||
- 能根据当前节点更新状态和撤销之前的更改
|
||||
- 得有一个 `&result` 来存放结果,这个 `&result` 通常是一个向量 `vector<NodeState> &`,里面存放了节点状态。
|
||||
- 其它传入参数用来完成每层遍历操作和每条边的操作。
|
||||
|
||||
设计完了数据结构之后来看看具体代码怎么写。模板如下:
|
||||
|
||||
```cpp
|
||||
void backtrack(NodeState &node, vector<NodeState> &result, int para1, int para2, int para3) {
|
||||
// 终止条件
|
||||
// 回溯法中的每个节点并不是真的树状节点,没有 `nullptr` ,因此用空指针来判断是否到了叶子结点并不合理,需要其它的一些方法来确定是否到达叶子节点,比如高度。
|
||||
if (/* end condition */) {
|
||||
result.push_back(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历该节点的所有子节点,即遍历下一层
|
||||
for (...) {
|
||||
// 剪枝
|
||||
// 当现在的节点不可能出现我们想要的结果时,直接跳过。
|
||||
if (/* out of scope */) {
|
||||
continue;
|
||||
}
|
||||
// 处理节点
|
||||
// 现在 node 中的数据描述的是当前节点,
|
||||
// handle(node) 一般是让 node 中的数据变成子节点的数据
|
||||
handle(node);
|
||||
// 递归
|
||||
backtrack(node, result, para1, para2, para3);
|
||||
// 撤销数据处理,让 node 中的数据再次变回描述当前节点的数据
|
||||
revert(node);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
复杂度分析:
|
||||
|
||||
|
5
notes/src/split.md
Normal file
5
notes/src/split.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 切割问题
|
||||
|
||||
## [131. 分割回文串](https://leetcode.cn/problems/palindrome-partitioning/)
|
||||
|
||||
![](https://paste.sainnhe.dev/FHTE.jpg)
|
40
src/s0131_palindrome_partitioning.cpp
Normal file
40
src/s0131_palindrome_partitioning.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "s0131_palindrome_partitioning.hpp"
|
||||
|
||||
bool isPalindrome(const string &s, int begin, int end) {
|
||||
for (int i = begin, j = end; i <= j; ++i, --j) {
|
||||
if (s[i] != s[j]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 回溯
|
||||
*
|
||||
* @param path 每个元素都是一个回文串
|
||||
* @param result 存放最终结果
|
||||
* @param s 待处理的字符串
|
||||
* @param startIndex s[0...startIndex - 1] 被分成了很多个子字符串,存放在 path 中
|
||||
*/
|
||||
void partitionDFS(vector<string> &path, vector<vector<string>> &result, const string &s, int startIndex) {
|
||||
int len = s.length();
|
||||
// 终止条件
|
||||
if (startIndex == len) {
|
||||
result.push_back(path);
|
||||
}
|
||||
|
||||
for (int i = startIndex; i < len; ++i) {
|
||||
// 如果 s[startIndex...i] 是回文串,则进入下一层
|
||||
if (isPalindrome(s, startIndex, i)) {
|
||||
path.push_back(s.substr(startIndex, i - startIndex + 1));
|
||||
partitionDFS(path, result, s, i + 1);
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<vector<string>> S0131::partition(string s) {
|
||||
vector<vector<string>> result{};
|
||||
vector<string> path{};
|
||||
partitionDFS(path, result, s, 0);
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue
Block a user