20. https://leetcode.com/problems/valid-parentheses/description/
678. https://leetcode.com/problems/valid-parenthesis-string/description/
32. https://leetcode.com/problems/longest-valid-parentheses/description/
22. https://leetcode.com/problems/generate-parentheses/description/
856. https://leetcode.com/problems/score-of-parentheses/description/
301. https://leetcode.com/problems/remove-invalid-parentheses/description/
921. https://leetcode.com/problems/minimum-add-to-make-parentheses-valid/description/
20 Valid Parentheses
Easy 题,判断括号是否匹配,显然用stack 解决,唯一需要注意的是如果stack 为空时 pop 会抛出异常,在抛出异常时说明不满足条件。
例: "()[]{}" 符合匹配。
try{ char c = st.pop(); } catch (Exception e){ return false; }
678. Valid Parenthesis String
这题为升级版的 20 Valida parenthesis,里面加了 *, * 可以匹配 一个 ( 或者 一个 ) 或者不匹配任何。
32. Longest Valid Parentheses
给定一组小括号求最长的匹配。
例: "( ) ( ( ) ( )" , 如果一开始遇到符合的匹配,但后面的匹配不满足条件 则 重新开始计算,本例中 最长的是 后面 4个括号,返回4.
算法: 本题属于hard 题, 用stack, 但stack 里存放的不是 括号,而是 括号的Index。
1. 现在 stack 里放入 -1
2. 如果遇到 "(" 就把 index push stack, 遇到 ")" 先 pop 然后 拿 " )" 的 index - stack.peek()。 假设 (), stack :- 1 ,0 , 在遇到第一个 ) 时 先 pop 0, 再 拿 ) 的index 1 - (-1) = 2. 但这个算法有个问题就是 假如 stack pop 后 为empty, 例如 “)” , stack -1, 遇到 ) 时, 先 pop -1, 此时 stack 为空, 因此在每次 index - stack.peek() 时 得先判断 stack 是否empty, 如果 empty 那么就把当前 ) 的 index push stack ,相当于 起始时插入的-1.
例如: ")()"
stack: -1
) : pop -1, 此时stack is empty, 因此 push 0,
( push 1
): pop 1, index 2 , 2 - stack.peek() = 2 -0 = 2. 因此遇到 ) 并且 stack 为 empty 时 把 ) 的index push , 相当于重新建立start index.
代码非常简单,只是 插入 index 到stack 里的算法很难想。
class Solution { public int longestValidParentheses(String s) { if(s == null || s.length() ==0) return 0; Stack<Integer> st = new Stack<>(); st.push(-1); int max = 0; for(int i=0; i<s.length(); i++){ if(s.charAt(i) == '(') st.push(i); else { st.pop(); if(st.isEmpty()){ st.push(i); } max = Math.max(max,i-st.peek()); } } return max; } }
22. Generate Parentheses
给定n , 生成所有 可能的 括号匹配的组合, 不要求输出括号的顺序。
例如 n = 3, 共有 如下的组合:
[ "((()))", "(()())", "(())()", "()(())", "()()()" ]
分析: 1. 任何时候都得先 放左括号 2. 任何时候 右括号 ) 个数都不能比( 个数多。
显然用DFS, 设计如下 DFS的函数接口:
dfs(int numLeft, int numRight, String str, List<String> result, int n)
dfs 终止条件 为 numRigth == n,
1. 当 numLeft == numRight 时, 只能放 左括号 (
2. 当 numLeft > numRight时 有两种情况
2.1 放右括号 )
2.2 当 numLeft<n 时, 可以放左括号 (. 第一次的code 忘记了加判断条件 numLeft<n 结果 内存溢出了。
dfs 调用 dfs(1,0, "(", result, n), code 如下:
class Solution { public List<String> generateParenthesis(int n) { List<String> result = new ArrayList<>(); helper(1,0,"(", n, result); return result; } private void helper(int numLeft, int numRight, String str, int n, List<String> result){ if(numRight == n){ result.add(str); } else if(numLeft == numRight){ // add ( helper(numLeft+1, numRight, str+"(", n,result); } else if(numLeft > numRight){ helper(numLeft, numRight+1, str+")",n,result); if(numLeft<n) helper(numLeft+1, numRight, str+"(", n,result); } } }
此题虽然为简单 DFS, 但有多种DFS 的写法,待分析。
921. Minimum Add to Make Parentheses Valid
问你最少添加多少个括号让括号字符串变成 balanced.
例如 "())", ans = 1
")))" ans = 3
"(()(" ans = 2
算法: 构造一个stack, 遇到left ( 放入, 遇到 right ) 就pop, 在pop 前判断 stack 是否为empty ,如果是说明 left 少了一个括号, ans ++
扫描完字符串后 再统计 stack 里还剩多少个 (, 说明还缺这么多个 right ) , return ans + stack.size()
code 也非常简单:
class Solution { public int minAddToMakeValid(String S) { Stack<Character> st = new Stack<>(); int sum = 0; for(char c: S.toCharArray()){ if(c == '(') st.push('('); else { if(st.isEmpty()) sum++; else st.pop(); } } return sum+=st.size(); } }
改进: solution 里给的算法, 不需要额外的stack, space 为O(1) ,但思想和上面用stack 的是一致的。
遇到 ( ans +1, 遇到 ) 再 ans -1, 这样如果本身是balanced 最后 ans ==0.
1. 在每次 发现ans == -1 时,说明 多了个 right ) , 这样 bla ++, 并且把 ans 重新置0
2. 最后的结果中 ans 为额外的 left (, bla 为额外的 ), 加一起就是需要的result
class Solution { public int minAddToMakeValid(String S) { int ans = 0; int bla = 0; for(char c: S.toCharArray()){ ans += c =='(' ? 1:-1; if(ans == -1){ // 说明多了 ) 括号, 因此 bla ++, 然后 ans 重新回0 ans = 0; bla++; } } return ans+bla; // 最后ans 的个数为额外的 (, bla 为多出的 ). 例如 ))) (( , 那么 bla = 3, ans = 2 } }
856. Score of Parentheses
()
has score 1AB
has scoreA + B
, where A and B are balanced parentheses strings.(A)
has score2 * A
, where A is a balanced parentheses string.
Input: "(()(()))" Output: 6
算法一: 参考solution 中 stack 的解法,不管什么组合 本质都是由 add + sum*2 组合而成的。
特例: 如果是 () 可以认为是 0+ 2*0 =0, 直接赋值为1.
算法就2步:
1. 初始化: push 0 to stack, 可以认为一开始的加数为0
2. 在遇到 left ( 时往 stack 里放0, 在遇到 right ) 从stack 里 pop 两次, 第一个pop 为sum, 计算 2*sum, 第二个 pop 为add
例如 (()) , 1. 0, 2. ( 0 0 3. (( 0 0 0, 4. ) , pop 出两个0, 第一次 2*0+0 ==0, 为特例 记作1. 将sum push 回 stack, 此时 stack 为 0,1 5, ) 从stack 里 pop 两次 , 变成 2*1 +0 = 2.
例如 () (()), 1. 遇到第一个 () 后, stack 为 1, 2. ( ( stack 为 1, 0,0 3. ) pop 两次 计算结果后 stack 为 1,1 4.) pop 出两个1, 计算结果为 1+2*1 = 3.
class Solution { public int scoreOfParentheses(String S) { Stack<Integer> stack = new Stack<>(); stack.push(0); //int score = 0; for(char c: S.toCharArray()){ if(c == '(') stack.push(0); else { int sum = stack.pop(); int add = stack.pop(); int cur_score = Math.max(sum*2,1)+add; stack.push(cur_score); } } return stack.pop(); } }
算法二: 同样用stack 只是 stack里存放 是左右括号。
算法: 只有乘法和加法运算,可以表示成例如: A(B+C(D+E)) 对于这样只有乘法和加法的运算,不一定要从括号里面先算,而是可以 等价成 AB+ AC*D+AC*E
任何时候遇到一个封闭的一对括号 () 可以先看外面嵌套了基层括号, 直接 拿2^层数, 下一个 封闭的() 也是如此 再加上一步的和。
具体算法: 用一个stack, 遇到 “(” 就入栈, 遇到 ) 时,先直接 pop,再 看 index -1 时 是否是(, 如果是 则可以组成完整的 (), 然后stack 的size 就是层数, 如果 index- 1 也是) 则直接忽略就可以。
例如 (( () () ) ) , 1. 遇到 第一个) 时, stack 里有3个 (, 先pop 一次 剩下2个( , 因为 上一个字符是 (, 此时 ans += 2^stack_size = 4, 2. 遇到 第二个 ) 时, 先pop 后 ,此时 stack 里还有两个(, 然后 ans += 2^stack_size = 8, 3. 最后一个) 时,因为上一个是) 先pop ,然后直接跳过。
class Solution { public int scoreOfParentheses(String S) { Stack<Character> stack = new Stack<>(); int ans = 0; for(int i=0; i<S.length(); i++){ if(S.charAt(i) == '(') stack.push('('); else{ stack.pop(); if(S.charAt(i-1) == '(') ans += Math.pow(2,stack.size()); //else 如果是 )) 则忽略 } } return ans; } }