Skip to content

Commit

Permalink
modify dfs
Browse files Browse the repository at this point in the history
  • Loading branch information
puppylpg committed May 6, 2024
1 parent 554decc commit 2323bef
Showing 1 changed file with 114 additions and 0 deletions.
114 changes: 114 additions & 0 deletions _tutorials/2024-05-04-dfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,119 @@ class Solution {
```
**所以剪枝确实很考验思维。我们也应该记住,修改(比如sort)源数据是个可以考虑的方案。**

**在括号相关的题目中,左括号要始终>=右括号**,否则字符串就不符合要求了。如果一个左括号score+1,一个右括号score-1,那么score要时刻保持大于0,用这一点剪枝非常有效。比如[删除无效的括号](https://leetcode.cn/problems/remove-invalid-parentheses/description/):
```java
class Solution {
public List<String> removeInvalidParentheses(String s) {
// remove Left, remove Right
int rl = 0, rr = 0, n = s.length();

int curL = 0, curR = 0, totalL = 0, totalR = 0;
for (char c : s.toCharArray()) {
if (c == '(') {
curL++;
totalL++;
}
if (c == ')') {
curR++;
totalR++;
}

if (curR > curL) {
rr++;
curR--;
}
}

rl = curL - curR;

// print("rl", rl);print("rr", rr);

Set<String> result = new HashSet<>();

dfs(result, "", rl, rr, 0, s, 0);

return new ArrayList<>(result);
}

private void dfs(Set<String> result, String cur, int rl, int rr, int index, String raw, int score) {
// 剪枝:如果左右已经不对了,没必要继续下去
if (score < 0) {
return;
}

// print("rl", rl);print("rr", rr);print("index", index);print("cur", cur);print("charAt", raw.charAt(index));
if (rl == 0 && rr == 0) {
if (isValid(cur + raw.substring(index))) {
result.add(cur + raw.substring(index));
return;
}

// 在增长的过程中,不必check cur是否有效,还没增加完,很可能是无效的
// 在rl = rr = 0的时候做校验,这是一种剪枝
if (!isValid(cur)) {
return;
}
}

// 这个不算剪枝,已经到头了,只能算最后的无效字符串过滤条件,不如换成下面这个剪枝1
if (index == raw.length()) {
return;
}

// 剪枝1:如果剩余的字符不够删了,提前结束
if (rl + rr > raw.length() - index) {
return;
}

if (rl > 0 && raw.charAt(index) == '(') {
dfs(result, cur, rl - 1, rr, index + 1, raw, score);
}

if (rr > 0 && raw.charAt(index) == ')') {
dfs(result, cur, rl, rr - 1, index + 1, raw, score);
}

if (raw.charAt(index) == '(') {
score++;
} else if (raw.charAt(index) == ')') {
score--;
}
dfs(result, cur + raw.charAt(index), rl, rr, index + 1, raw, score);

// 恢复上下文
if (raw.charAt(index) == '(') {
score--;
} else if (raw.charAt(index) == ')') {
score++;
}
}

private boolean isValid(String s) {
int curL = 0, curR = 0;
for (char c : s.toCharArray()) {
if (c == '(') {
curL++;
}
if (c == ')') {
curR++;
}

if (curR > curL) {
return false;
}
}

return curL == curR;
}
}
```
不剪枝/加上剪枝1/再加上score剪枝,耗时分别为:522/36/13ms。

但是注意这里score=0并不能作为最终字符串是否有效的标志,因为最后一段是直接用substring拼接上去的,没有计算score。**score只是标记了当前生成中的字符串是否是合法的**

本题递归空间为2^n(相当于子集判断),生成字符串以后要再叠加isValid判断,所以是O(n * 2^n)。

## 题目类型:子集
单独[子集](https://leetcode.cn/problems/subsets/description/)类型的题目拉出来,是因为发现时隔不久之后,**再写回溯的时候就“心中没树”了**!所以再拉出来把解题模板强化一遍。

Expand Down Expand Up @@ -857,3 +970,4 @@ class Solution {
- 返回值(返回方式)

**通用dfs基本都是前序。如果是树,dfs的时候考虑一下要不要用后序(从下往上思考问题)。**

0 comments on commit 2323bef

Please sign in to comment.