[LeetCode] Remove Invalid Parentheses 移除非法括号
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.
Note: The input string may contain letters other than the parentheses ( and ).
Example 1:
Input: "()())()"
Output: ["()()()", "(())()"]
Example 2:
Input: "(a)())()"
Output: ["(a)()()", "(a())()"]
Example 3:
Input: ")("
Output: [""]
Credits:
Special thanks to @hpplayer for adding this problem and creating all test cases.
Subscribe to see which companies asked this question
解法一:
class Solution {
public:
vector<string> removeInvalidParentheses(string s) {
vector<string> res;
unordered_set<string> visited{{s}};
queue<string> q{{s}};
bool found = false;
while (!q.empty()) {
string t = q.front(); q.pop();
if (isValid(t)) {
res.push_back(t);
found = true;
}
if (found) continue;
for (int i = ; i < t.size(); ++i) {
if (t[i] != '(' && t[i] != ')') continue;
string str = t.substr(, i) + t.substr(i + );
if (!visited.count(str)) {
q.push(str);
visited.insert(str);
}
}
}
return res;
}
bool isValid(string t) {
int cnt = ;
for (int i = ; i < t.size(); ++i) {
if (t[i] == '(') ++cnt;
else if (t[i] == ')' && --cnt < ) return false;
}
return cnt == ;
}
};
下面来看一种递归解法,这种解法首先统计了多余的半括号的数量,用 cnt1 表示多余的左括号,cnt2 表示多余的右括号,因为给定字符串左右括号要么一样多,要么左括号多,要么右括号多,也可能左右括号都多,比如 ")("。所以 cnt1 和 cnt2 要么都为0,要么都大于0,要么一个为0,另一个大于0。好,下面进入递归函数,首先判断,如果当 cnt1 和 cnt2 都为0时,说明此时左右括号个数相等了,调用 isValid 子函数来判断是否正确,正确的话加入结果 res 中并返回即可。否则从 start 开始遍历,这里的变量 start 表示当前递归开始的位置,不需要每次都从头开始,会有大量重复计算。而且对于多个相同的半括号在一起,只删除第一个,比如 "())",这里有两个右括号,不管删第一个还是删第二个右括号都会得到 "()",没有区别,所以只用算一次就行了,通过和上一个字符比较,如果不相同,说明是第一个右括号,如果相同则直接跳过。此时来看如果 cnt1 大于0,说明此时左括号多,而如果当前字符正好是左括号的时候,可以删掉当前左括号,继续调用递归,此时 cnt1 的值就应该减1,因为已经删掉了一个左括号。同理,如果 cnt2 大于0,说明此时右括号多,而如果当前字符正好是右括号的时候,可以删掉当前右括号,继续调用递归,此时 cnt2 的值就应该减1,因为已经删掉了一个右括号,参见代码如下:
解法二:
class Solution {
public:
vector<string> removeInvalidParentheses(string s) {
vector<string> res;
int cnt1 = , cnt2 = ;
for (char c : s) {
cnt1 += (c == '(');
if (cnt1 == ) cnt2 += (c == ')');
else cnt1 -= (c == ')');
}
helper(s, , cnt1, cnt2, res);
return res;
}
void helper(string s, int start, int cnt1, int cnt2, vector<string>& res) {
if (cnt1 == && cnt2 == ) {
if (isValid(s)) res.push_back(s);
return;
}
for (int i = start; i < s.size(); ++i) {
if (i != start && s[i] == s[i - ]) continue;
if (cnt1 > && s[i] == '(') {
helper(s.substr(, i) + s.substr(i + ), i, cnt1 - , cnt2, res);
}
if (cnt2 > && s[i] == ')') {
helper(s.substr(, i) + s.substr(i + ), i, cnt1, cnt2 - , res);
}
}
}
bool isValid(string t) {
int cnt = ;
for (int i = ; i < t.size(); ++i) {
if (t[i] == '(') ++cnt;
else if (t[i] == ')' && --cnt < ) return false;
}
return cnt == ;
}
};
下面这种解法是论坛上的高票解法,思路确实很巧妙。递归函数的参数中,last_i 表示当前遍历到的位置,相当上面解法中的 start,last_j 表示上一个删除的位置,这样可以避免重复计算。然后有个括号字符数组,初始化时放入左括号和右括号,博主认为这个字符数组是此解法最精髓的地方,因为其顺序可以改变,可以变成反向括号,这个就比较叼了,后面再讲它到底有多叼吧。在递归函数中,从 last_i 开始遍历,在找正向括号的时候,用变量 cnt 表示括号数组中的左括号出现的次数,遇到左括号自增1,遇到右括号自减1。当左括号大于等于右括号的时候,直接跳过。这个循环的目的是要删除多余的右括号,所以当 cnt 小于0的时候,从上一个删除位置 last_j 开始遍历,如果当前是右括号,且是第一个右括号(关于这块可以参见上面解法中的分析),删除当前右括号,并调用递归函数。注意这个 for 循环结束后要直接返回,因为进这个 for 循环的都是右括号多的,删到最后最多是删成和左括号一样多,不需要再去翻转删左括号。好,最后来说这个最叼的翻转,当字符串的左括号个数大于等于右括号的时候,不会进入第二个 for 循环,自然也不会 return。那么由于左括号的个数可能会要大于右括号,所以还要删除多余的左括号,将字符串反转一下,比如 "(()",反转变成 ")((",此时虽然还是要删除多余的左括号,但是反转后就没有合法的括号了,所以变成了找反向括号 ")(",还是可以删除多余的左括号,然后判断此时括号数组的状态,如果是正向括号,说明此时正要删除左括号,就调用递归函数,last_i 和 last_j 均重置为0,括号数组初始化为反向括号。如果此时已经是反向括号了,说明之前的左括号已经删掉了变成了 ")(",然后又反转了一下,变回来了 "()",就可以直接加入结果 res 了,参见代码如下:
解法三:
class Solution {
public:
vector<string> removeInvalidParentheses(string s) {
vector<string> res;
helper(s, , , {'(', ')'}, res);
return res;
}
void helper(string s, int last_i, int last_j, vector<char> p, vector<string>& res) {
int cnt = ;
for (int i = last_i; i < s.size(); ++i) {
if (s[i] == p[]) ++cnt;
else if (s[i] == p[]) --cnt;
if (cnt >= ) continue;
for (int j = last_j; j <= i; ++j) {
if (s[j] == p[] && (j == last_j || s[j] != s[j - ])) {
helper(s.substr(, j) + s.substr(j + ), i, j, p, res);
}
}
return;
}
string rev = string(s.rbegin(), s.rend());
if (p[] == '(') helper(rev, , , {')', '('}, res);
else res.push_back(rev);
}
};
下面这种解法由热心网友 fvglty 提供,应该算是一种暴力搜索的方法,并没有太多的技巧在里面,但是思路直接了当,可以作为为面试中最先提出的解法。思路是先将s放到一个 HashSet 中,然后进行该集合 cur 不为空的 while 循环,此时新建另一个集合 next,遍历之前的集合 cur,若某个字符串是合法的括号,直接加到结果 res 中,并且看若 res 不为空,则直接跳过。跳过的部分实际上是去除括号的操作,由于不知道该去掉哪个半括号,所以只要遇到半括号就都去掉,然后加入另一个集合 next 中,这里实际上保存的是下一层的候选者。当前的 cur 遍历完成后,若 res 不为空,则直接返回,因为这是当前层的合法括号,一定是移除数最少的。若 res 为空,则将 next 赋值给 cur,继续循环,参见代码如下:
解法四:
class Solution {
public:
vector<string> removeInvalidParentheses(string s) {
vector<string> res;
unordered_set<string> cur{{s}};
while (!cur.empty()) {
unordered_set<string> next;
for (auto &a : cur) {
if (isValid(a)) res.push_back(a);
if (!res.empty()) continue;
for (int i = ; i < a.size(); ++i) {
if (a[i] != '(' && a[i] != ')') continue;
next.insert(a.substr(, i) + a.substr(i + ));
}
}
if (!res.empty()) return res;
cur = next;
}
return res;
}
bool isValid(string t) {
int cnt = ;
for (int i = ; i < t.size(); ++i) {
if (t[i] == '(') ++cnt;
else if (t[i] == ')' && --cnt < ) return false;
}
return cnt == ;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/301
类似题目:
Different Ways to Add Parentheses
参考资料:
https://leetcode.com/problems/remove-invalid-parentheses/
https://leetcode.com/problems/remove-invalid-parentheses/discuss/75032/share-my-java-bfs-solution
[LeetCode] Remove Invalid Parentheses 移除非法括号的更多相关文章
- [LeetCode] 301. Remove Invalid Parentheses 移除非法括号
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all ...
- 301 Remove Invalid Parentheses 删除无效的括号
删除最小数目的无效括号,使输入的字符串有效,返回所有可能的结果.注意: 输入可能包含了除 ( 和 ) 以外的元素.示例 :"()())()" -> ["()()() ...
- [LeetCode] Remove Invalid Parentheses
This problem can be solved very elegantly using BFS, as in this post. The code is rewritten below in ...
- Leetcode之深度优先搜索(DFS)专题-301. 删除无效的括号(Remove Invalid Parentheses)
Leetcode之深度优先搜索(DFS)专题-301. 删除无效的括号(Remove Invalid Parentheses) 删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果. 说明 ...
- [LeetCode] Longest Valid Parentheses 最长有效括号
Given a string containing just the characters '(' and ')', find the length of the longest valid (wel ...
- [leetcode]301. Remove Invalid Parentheses 去除无效括号
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all ...
- LeetCode 301. Remove Invalid Parentheses
原题链接在这里:https://leetcode.com/problems/remove-invalid-parentheses/ 题目: Remove the minimum number of i ...
- [Swift]LeetCode301. 删除无效的括号 | Remove Invalid Parentheses
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all ...
- 301. Remove Invalid Parentheses去除不符合匹配规则的括号
[抄题]: Remove the minimum number of invalid parentheses in order to make the input string valid. Retu ...
随机推荐
- xss和csrf攻击
xss(cross site scripting)是一种最常用的网站攻击方式. 一.Html的实体编码 举个栗子:用户在评论区输入评论信息,然后再评论区显示.大概是这个样子: <span> ...
- Spark的DataFrame的窗口函数使用
作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 SparkSQL这块儿从1.4开始支持了很多的窗口分析函数,像row_number这些,平时写程 ...
- ASP.NET Core 中文文档 第三章 原理(12)托管
原文:Hosting 作者:Steve Smith 翻译:娄宇(Lyrics) 校对:何镇汐.许登洋(Seay) 为了运行 ASP.NET Core 应用程序,你需要使用 WebHostBuilder ...
- js正则表达式整理
一.数字类 数字:^[0-9]*$ 正数.负数.和小数:^(\-|\+)?\d+(\.\d+)?$ 零和非零开头的数字:^(0|[1-9][0-9]*)$ 非零开头的最多带两位小数的数字:^([1-9 ...
- StringBuilder的使用
今天用到了StringBuilder来拼接查询语句,发现这个真好用,决定做个小结. 百度一个StringBuilder的定义:String 对象是不可改变的.每次使用 System.String 类中 ...
- 用canvas绘制折线图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- BPM实例分享——日期自动计算
日期自动计算 在请假流程中通常我们需要获得请假开始到请假结束时间的天数,那么请假天数(可结合工作日历)是怎么实现系统计算呢?下面我们来看下配置的方法. 1. 首选建立业务服务BPMService, ...
- Scala 包
包的绝对地址_root_.开始 如_root_.scala.collection.mutable.ArrayBuffer
- 用AVFoundation自定义相机拍照
自定义拍照或者录视频的功能,就需要用到AVFoundation框架,目前我只用到了拍照,所以记录下自定义拍照用法,视频用法等用上了再补充,应该是大同小异 demo在这里:https://github. ...
- android Content Provider介绍
ContentProvider(内容提供者)是Android中的四大组件之一.主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过Conte ...