有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “()[]{}”
输出:true
示例 3:
输入:s = “(]”
输出:false
示例 4:
输入:s = “([)]”
输出:false
示例 5:
输入:s = “{[]}”
输出:true
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
这题的思路就是用一个hashmap来存放闭括号和其对应的开括号。接下来我们就可以遍历字符串了。我们用一个栈来存放遍历到的括号,如果是开括号那么就直接压入栈。如果是闭括号,那么我们就去hashmap找到他对应的开括号,接下来在查看栈顶元素,如果对应的上说明这一对括号没问题,于是将栈顶的括号弹出,并继续往下遍历。如果对应不上,说明这一对括号有问题。
此外在题目开始做奇偶判定,如果字符串的长度是奇数,那么肯定是凑不到有效括号的。
代码
class Solution {
public boolean isValid(String s) {
int n = s.length();
if(n % 2 == 1){
return false;
}
Deque<Character> fuhao = new ArrayDeque<>();
Map<Character,Character> duizhao = new HashMap<Character,Character>(){{
put(')','(');
put(']','[');
put('}','{');
}};
for(int i = 0;i < n;i++){
char nowc = s.charAt(i);
if(duizhao.containsKey(nowc)){
if(fuhao.isEmpty() || fuhao.peekLast() != duizhao.get(nowc)){
return false;
}
fuhao.removeLast();
}else{
fuhao.addLast(nowc);
}
}
return fuhao.isEmpty();
}
}
括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
这题要用dfs来做,不断的递归调用。递归调用的判断条件是开括号和闭括号的数量关系,递归的结束条件是字符串的长度已经达到了指定长度。
代码
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
if(n <= 0) return res;
getans(n ,"" ,res ,0 , 0 );
return res;
}
public void getans(int n ,String str ,List<String> res ,int open ,int close ){
if(str.length() == 2 * n){
res.add(str);
return;
}
if(open < n){
getans(n ,str + "(" ,res ,open + 1 ,close);
}
if(open > close){
getans(n ,str + ")" ,res ,open , close + 1);
}
}
}
删除无效括号
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
示例 1:
输入:s = “()())()”
输出:["(())()","()()()"]
示例 2:
输入:s = “(a)())()”
输出:["(a())()","(a)()()"]
示例 3:
输入:s = “)(”
输出:[""]
提示:
1 <= s.length <= 25
s 由小写英文字母以及括号 '(' 和 ')' 组成
s 中至多含 20 个括号
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-invalid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
首先计算最少需要删除的开括号和闭括号的数量。遍历的时候,遇到左括号就直接让leftremove的数量加一。遇到右括号,那么就先判定左括号的数量,如果这时候左括号的数量大于0,那么说明在之前的字符串中有至少一个多余的左括号可以和这个右括号配对成一个完整的括号,那么我们就直接让leftremove减一就可以了,如果这时候左括号的数量为0,说明在之前的字符串中已经没有多余的左括号了,那么我们就只能让rightremove加一。
接下来就是dfs了。结束递归的条件是字符串已经遍历完了。递归的时候先是递归删除左右括号的情况,删除的条件是leftremove或者rightremove还有余量。在这两种情况递归之后,还有保留左右括号的情况需要递归。需要注意的是,保留右括号的时候需要判断此时左括号的数量是否大于右括号的数量,如果大于才能保留下来这个左括号。
递归过程的图解参照
https://leetcode-cn.com/problems/remove-invalid-parentheses/solution/shan-chu-wu-xiao-de-gua-hao-by-leetcode/
代码
class Solution {
char[] chararray;
int len;
HashSet<String> set = new HashSet<>();
public List<String> removeInvalidParentheses(String s) {
len = s.length();
chararray = s.toCharArray();
int leftRemove = 0;
int rightRemove = 0;
for (int i = 0; i < len; i++) {
if (chararray[i] == '(') {
leftRemove++;
} else if (chararray[i] == ')') {
if (leftRemove > 0) {
leftRemove--;
} else {
rightRemove++;
}
}
}
StringBuilder builder = new StringBuilder();
dfs(0, 0, 0, leftRemove, rightRemove, builder);
return new ArrayList<String>(this.set);
}
public void dfs(int index, int leftcount, int rightcount, int leftRemove, int rightRemove, StringBuilder builder) {
if (index == len) {
if (leftRemove == 0 && rightRemove == 0) {
set.add(builder.toString());
}
return;
}
char Character = chararray[index];
if (Character == '(' && leftRemove > 0) {
dfs(index + 1, leftcount, rightcount, leftRemove - 1, rightRemove, builder);
} else if (Character == ')' && rightRemove > 0) {
dfs(index + 1, leftcount, rightcount, leftRemove, rightRemove - 1, builder);
}
builder.append(Character);
if (Character != '(' && Character != ')') {
dfs(index + 1, leftcount, rightcount, leftRemove, rightRemove, builder);
} else if (Character == '(') {
dfs(index + 1, leftcount + 1, rightcount, leftRemove, rightRemove, builder);
} else if (leftcount > rightcount){
dfs(index + 1, leftcount, rightcount + 1, leftRemove, rightRemove, builder);
}
builder.deleteCharAt(builder.length() - 1);
}
}
最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:
输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:
输入:s = “”
输出:0
提示:
0 <= s.length <= 3 * 104
s[i] 为 '(' 或 ')'
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
用动态规划来完成。状态的定义是以i结尾的字符串的最长有效括号即为dp【i】。如果角标i位置的字符是)才会进行接下来最长有效括号的判定,因为(结尾的话,最长有效括号是没有意义的。
接下来我们计算preindex,preindex就是这个位置的),对应的(应该在的位置。计算就是角标i减去dp【i - 1】,也就是减去上个位置如果有完整的括号的话的括号数量,接下来再减一,也就得到了(应该在的位置。如果preindex的字符恰好就是(,那么说明可以形成一个有效的括号。所以我们在predp的基础上加上这一对括号。接下来我们就要知道再之前的位置有没有有效的括号,而这一步我们需要通过dp数组中角标preindex - 1来知道。
计算完成之后也就得到了dp【i】的值
代码
class Solution {
public int longestValidParentheses(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int max = 0;
char[] array = s.toCharArray();
int[] dp = new int[array.length];
for (int i = 1; i < array.length; i++) {
if (array[i] == ')') {
int predp = dp[i - 1];
int preindex = i - predp - 1;
if (preindex >= 0 && array[preindex] == '(') {
dp[i] = dp[i - 1] + 2;
if (preindex - 1 >= 0) {
dp[i] += dp[preindex - 1];
}
}
}
max = Math.max(max, dp[i]);
}
return max;
}
}
这里有一个完整注释版的代码,来源于评论区,参照来看应该就问题不大。感谢评论区老哥
// 有效括号的最长长度
// 子串问题:严格以每个结尾计算个答案,最终答案必在其中
public static int longestValidParentheses(String s) {
if (s == null || s.length() < 2) return 0;
int[] dp = new int[s.length()]; // dp[i]:严格以i位置结尾,形成的有效括号子串最长长度是多少
int max = 0; // 最终的答案
// dp[0] = 0; // 默认
for (int i = 1; i < s.length(); i++) {
// if (s.charAt(i) == '(') dp[i] = 0; 以左括号结尾,无效
if (s.charAt(i) == ')') {
int preLen = dp[i - 1]; // 前面已经形成的有效括号长度
int pre = i - 1 - preLen; // 寻找与当前的右括号相匹配的左括号位置:前面有效括号长度再往前一个位置
if (pre >= 0 && s.charAt(pre) == '(') { // 如果寻找到左括号:前面有效括号长度再往前一个位置是左括号
dp[i] = dp[i-1] + 2; // 可以与当前的右括号闭合,有效长度增加2
// 【注意】此时,需要再往前看下,是否还有有效长度,如果有,合并过来
// 例如:"()(()())" 当前在计算最后一个位置时,dp[7]已经等于 dp[6]+2 = 4+2
// 但需要再往前看一眼,dp[1]还有有效长度,合并过来 dp[7] = 4+2+2
// 那是否还需要再往前看?
// 不需要了,因为,如果前面还有有效长度,其长度肯定已经合并到dp[2]上了
// 因此,每次只需要再往前多看一眼就可以
if (pre-1 >= 0) {
dp[i] += dp[pre-1];
}
}
max = Math.max(max, dp[i]); // 严格以每个结尾抓一个答案,最终答案必在其中
}
}
return max;
}