Skip to content

Commit 6417924

Browse files
committed
301.删除无效的括号,回溯
1 parent f28374a commit 6417924

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

leetcode_Java/DoneTitle.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
283. 移动零
9797
287. 寻找重复数
9898
300. 最长上升子序列
99+
301. 删除无效的括号
99100
303. 区域和检索 - 数组不可变
100101
304. 二维区域和检索 - 矩阵不可变
101102
309. 最佳买卖股票时机含冷冻期

leetcode_Java/DoneType.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
93. 复原 IP 地址
4949
131. 分割回文串
5050
216. 组合总和 III
51+
301. 删除无效的括号
5152
491. 递增子序列
5253
494. 目标和
5354
698. 划分为k个相等的子集

leetcode_Java/Solution0301.java

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// 301. 删除无效的括号
2+
3+
4+
/*
5+
回溯:
6+
1、题目要求将所有最长合法方案输出,因此不可能有别的优化,只能进行「爆搜」,可以使用 DFS 实现回溯搜索
7+
2、所有的合法方案,必然有左括号的数量与右括号数量相等。令左括号的得分为 1;右括号的得分为 −1。则合法方案会有如下性质:
8+
1)对于一个合法的方案而言,必然有最终得分为 0;
9+
2)搜索过程中不会出现得分值为 负数 的情况,即右括号数量大于左括号数量时为非法
10+
3、预处理出「爆搜」过程的最大得分:maxScore = min(左括号的数量, 右括号的数量),即合法左括号先全部出现在左边,之后使用最多的合法右括号进行匹配
11+
4、枚举过程中出现字符分三种情况:
12+
1)左括号:如果增加当前 ( 后,仍为合法子串,即 score+1 <= maxscore 时,我们可以选择添加该左括号,也能选择不添加
13+
2)右括号:如果增加当前 ) 后,仍为合法子串,即 score-1 >= 0 时,我们可以选择添加该右括号,也能选择不添加
14+
3)普通字符:直接添加
15+
5、使用 Set 进行方案去重,maxLen 记录「爆搜」过程中的最大子串,然后只保留长度等于 maxLen 的子串
16+
6、回溯过程的公共变量作为成员变量,方法独有的变量作为局部变量
17+
7、定义递归函数:
18+
1)剪枝条件:分数score非法,即左括号或右括号的数量多了
19+
2)终止条件:遍历完字符串,判断 分数是否为0、子串长度是否大于等于当前最大长度,若大于最大长度则更新最大长度,并清空集合,最后将将子串加入集合
20+
3)递归回溯:遍历字符,调用递归方法构造并校验子串。由于变量的改变在入参处理,所以递归结束后 下一个递归参数仍然使用原变量处理,多个递归方法参数互不影响,即回溯
21+
4)入参变化:
22+
① 一次递归即遍历下一个字符,索引index加1
23+
② 添加左括号时,分数score加1,子串subs加上该字符。不添加时分数score不变,子串subs不变
24+
③ 添加右括号时,分数score减1,子串subs加上该字符。不添加时分数score不变,子串subs不变
25+
④ 字母必须添加,分数score不变,子串subs加上该字符
26+
*/
27+
class Solution {
28+
int n, maxScore, maxLen;
29+
String s;
30+
Set<String> set = new HashSet<>();
31+
public List<String> removeInvalidParentheses(String s) {
32+
this.s = s;
33+
n = s.length();
34+
int left = 0, right = 0;
35+
for (char c : s.toCharArray()) {
36+
if (c == '(') {
37+
left++;
38+
} else if (c == ')') {
39+
right++;
40+
}
41+
}
42+
maxScore = Math.min(left, right);
43+
track(0, 0, "");
44+
return new ArrayList<>(set);
45+
}
46+
47+
private void track(int index, int score, String subs) {
48+
if (score < 0 || score > maxScore) {
49+
return;
50+
}
51+
if (index == n) {
52+
if (score == 0 && subs.length() >= maxLen) {
53+
if (subs.length() > maxLen) {
54+
maxLen = subs.length();
55+
set.clear();
56+
}
57+
set.add(subs);
58+
}
59+
return;
60+
}
61+
char c = s.charAt(index);
62+
if (c == '(') {
63+
track(index + 1, score + 1, subs + String.valueOf(c));
64+
track(index + 1, score, subs);
65+
} else if (c == ')') {
66+
track(index + 1, score - 1, subs + String.valueOf(c));
67+
track(index + 1, score, subs);
68+
} else {
69+
track(index + 1, score, subs + String.valueOf(c));
70+
}
71+
}
72+
}
73+
74+
75+
/*
76+
1、上一解法是在搜索过程中去更新最后的 maxLen,但实际上可以通过预处理,得到最后的「应该删除的左括号数量」和「应该删掉的右括号数量」,来直接得到最终的 maxLen,因此可以多增加一层剪枝
77+
2、遍历字符串时,对左括号数量和右括号数量进行匹配抵消,最终剩下的数量就是应该删除的括号数量,由字符串长度 减去 应该删除的括号数量 得到 最终子串长度
78+
3、定义递归函数:
79+
1)剪枝条件:分数score非法,即左括号或右括号的数量多了。应该删除括号数量非法,即删多了
80+
2)校验合法条件:应该删除括号数量为0、子串长度等于最大长度,则将子串加入集合
81+
3)终止条件:遍历完字符串,索引等于字符串长度
82+
4)递归回溯:遍历字符,调用递归方法构造并校验子串。由于变量的改变在入参处理,所以递归结束后 下一个递归参数仍然使用原变量处理,多个递归方法参数互不影响,即回溯
83+
5)入参变化:
84+
① 一次递归即遍历下一个字符,索引index加1
85+
② 添加左括号时,分数score加1,子串subs加上该字符。不添加时分数score不变,子串subs不变
86+
③ 添加右括号时,分数score减1,子串subs加上该字符。不添加时分数score不变,子串subs不变
87+
④ 字母必须添加,分数score不变,子串subs加上该字符
88+
⑤ 不添加左括号时,应该删除左括号数量leftDel减1。添加时则不变
89+
⑥ 不添加右括号时,应该删除右括号数量rightDel减1。添加时则不变
90+
*/
91+
class Solution {
92+
int n, maxScore, maxLen;
93+
String s;
94+
Set<String> set = new HashSet<>();
95+
public List<String> removeInvalidParentheses(String s) {
96+
this.s = s;
97+
n = s.length();
98+
int left = 0, right = 0;
99+
int leftDel = 0, rightDel = 0;
100+
for (char c : s.toCharArray()) {
101+
if (c == '(') {
102+
left++;
103+
leftDel++;
104+
} else if (c == ')') {
105+
right++;
106+
if (leftDel != 0) {
107+
leftDel--;
108+
} else {
109+
rightDel++;
110+
}
111+
}
112+
}
113+
maxScore = Math.min(left, right);
114+
maxLen = n - leftDel - rightDel;
115+
track(0, 0, leftDel, rightDel, "");
116+
return new ArrayList<>(set);
117+
}
118+
119+
private void track(int index, int score, int leftDel, int rightDel, String subs) {
120+
if (leftDel < 0 || rightDel < 0 || score < 0 || score > maxScore) {
121+
return;
122+
}
123+
if (leftDel == 0 && rightDel == 0 && subs.length() == maxLen) {
124+
set.add(subs);
125+
}
126+
if (index == n) {
127+
return;
128+
}
129+
char c = s.charAt(index);
130+
if (c == '(') {
131+
track(index + 1, score + 1, leftDel, rightDel, subs + String.valueOf(c));
132+
track(index + 1, score, leftDel - 1, rightDel, subs);
133+
} else if (c == ')') {
134+
track(index + 1, score - 1, leftDel, rightDel, subs + String.valueOf(c));
135+
track(index + 1, score, leftDel, rightDel - 1, subs);
136+
} else {
137+
track(index + 1, score, leftDel, rightDel, subs + String.valueOf(c));
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)