Skip to content

Commit 2323bef

Browse files
committed
modify dfs
1 parent 554decc commit 2323bef

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

_tutorials/2024-05-04-dfs.md

+114
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,119 @@ class Solution {
490490
```
491491
**所以剪枝确实很考验思维。我们也应该记住,修改(比如sort)源数据是个可以考虑的方案。**
492492

493+
**在括号相关的题目中,左括号要始终>=右括号**,否则字符串就不符合要求了。如果一个左括号score+1,一个右括号score-1,那么score要时刻保持大于0,用这一点剪枝非常有效。比如[删除无效的括号](https://leetcode.cn/problems/remove-invalid-parentheses/description/):
494+
```java
495+
class Solution {
496+
public List<String> removeInvalidParentheses(String s) {
497+
// remove Left, remove Right
498+
int rl = 0, rr = 0, n = s.length();
499+
500+
int curL = 0, curR = 0, totalL = 0, totalR = 0;
501+
for (char c : s.toCharArray()) {
502+
if (c == '(') {
503+
curL++;
504+
totalL++;
505+
}
506+
if (c == ')') {
507+
curR++;
508+
totalR++;
509+
}
510+
511+
if (curR > curL) {
512+
rr++;
513+
curR--;
514+
}
515+
}
516+
517+
rl = curL - curR;
518+
519+
// print("rl", rl);print("rr", rr);
520+
521+
Set<String> result = new HashSet<>();
522+
523+
dfs(result, "", rl, rr, 0, s, 0);
524+
525+
return new ArrayList<>(result);
526+
}
527+
528+
private void dfs(Set<String> result, String cur, int rl, int rr, int index, String raw, int score) {
529+
// 剪枝:如果左右已经不对了,没必要继续下去
530+
if (score < 0) {
531+
return;
532+
}
533+
534+
// print("rl", rl);print("rr", rr);print("index", index);print("cur", cur);print("charAt", raw.charAt(index));
535+
if (rl == 0 && rr == 0) {
536+
if (isValid(cur + raw.substring(index))) {
537+
result.add(cur + raw.substring(index));
538+
return;
539+
}
540+
541+
// 在增长的过程中,不必check cur是否有效,还没增加完,很可能是无效的
542+
// 在rl = rr = 0的时候做校验,这是一种剪枝
543+
if (!isValid(cur)) {
544+
return;
545+
}
546+
}
547+
548+
// 这个不算剪枝,已经到头了,只能算最后的无效字符串过滤条件,不如换成下面这个剪枝1
549+
if (index == raw.length()) {
550+
return;
551+
}
552+
553+
// 剪枝1:如果剩余的字符不够删了,提前结束
554+
if (rl + rr > raw.length() - index) {
555+
return;
556+
}
557+
558+
if (rl > 0 && raw.charAt(index) == '(') {
559+
dfs(result, cur, rl - 1, rr, index + 1, raw, score);
560+
}
561+
562+
if (rr > 0 && raw.charAt(index) == ')') {
563+
dfs(result, cur, rl, rr - 1, index + 1, raw, score);
564+
}
565+
566+
if (raw.charAt(index) == '(') {
567+
score++;
568+
} else if (raw.charAt(index) == ')') {
569+
score--;
570+
}
571+
dfs(result, cur + raw.charAt(index), rl, rr, index + 1, raw, score);
572+
573+
// 恢复上下文
574+
if (raw.charAt(index) == '(') {
575+
score--;
576+
} else if (raw.charAt(index) == ')') {
577+
score++;
578+
}
579+
}
580+
581+
private boolean isValid(String s) {
582+
int curL = 0, curR = 0;
583+
for (char c : s.toCharArray()) {
584+
if (c == '(') {
585+
curL++;
586+
}
587+
if (c == ')') {
588+
curR++;
589+
}
590+
591+
if (curR > curL) {
592+
return false;
593+
}
594+
}
595+
596+
return curL == curR;
597+
}
598+
}
599+
```
600+
不剪枝/加上剪枝1/再加上score剪枝,耗时分别为:522/36/13ms。
601+
602+
但是注意这里score=0并不能作为最终字符串是否有效的标志,因为最后一段是直接用substring拼接上去的,没有计算score。**score只是标记了当前生成中的字符串是否是合法的**
603+
604+
本题递归空间为2^n(相当于子集判断),生成字符串以后要再叠加isValid判断,所以是O(n * 2^n)。
605+
493606
## 题目类型:子集
494607
单独[子集](https://leetcode.cn/problems/subsets/description/)类型的题目拉出来,是因为发现时隔不久之后,**再写回溯的时候就“心中没树”了**!所以再拉出来把解题模板强化一遍。
495608

@@ -857,3 +970,4 @@ class Solution {
857970
- 返回值(返回方式)
858971

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

0 commit comments

Comments
 (0)