括号系列的几道题,涉及到括号匹配,生成括号等等.
Valid Parentheses
Given a string containing just the characters '('
, ')'
, '{'
, '}'
, '['
and ']'
, determine if the input string is valid.
An input string is valid if:
- Open brackets must be closed by the same type of brackets.
- Open brackets must be closed in the correct order.
Note that an empty string is also considered valid.
Example 1:
1 | Input: "()" |
Example 2:
1 | Input: "()[]{}" |
Example 3:
1 | Input: "(]" |
Example 4:
1 | Input: "([)]" |
Example 5:
1 | Input: "{[]}" |
使用一个栈来完成括号的匹配.当遇到一个左括号,入栈;当遇到一个右括号时,查看栈中是否存在对应的左括号.正常的状态是:左右括号的类型和数量都匹配,那么,当整个字符串遍历完成时,栈为空.因此,首先当字符串为空,该字符串必然是valid.若字符串中字符总数为奇数,该字符串必然为invalid.在遍历过程中,如果发现某个右括号不匹配,直接结束遍历过程,并将标记变量改为invalid.遍历结束之后,如果标记变量为valid,此时还需要检查栈是否为空.
代码如下:
1 | class Solution { |
时间复杂度:O(n)
空间复杂度:O(1)
Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
1 | [ |
解法1:暴力法
依次枚举每个位置上的字符: 对于任意位置i,先将该位置上的字符设为’(‘,然后继续枚举下一个位置,直到所有位置都被枚举完成,判断形成的字符是否有效.处理完第i位上为’(‘之后,继续枚举第i位上为’)’.只需从第0位开始枚举, 将每个位置上都枚举完成,所有可能的字符串都枚举完成,也找出了所有有效的字符串.显然需要递归实现上述思路.
1 | bool vaild(string &res) { |
复杂度分析
- 时间复杂度
- 当给出n时, 所有可能的字符串有22 * n个. 这是因为共2 * n 个位置上,每个位置上有两种可能.
- 对于每一个字符串,枚举该字符串并检查该字符串的有效性的时间复杂度为O(n), 因此总的时间复杂度为O(n* 22 * n)
- 空间复杂度
- 如上所示,整个递归过程中,只用了一个string,在这个string中,最多只会存放长度为2n的字符串,因此,空间复杂度为O(n)
解法2: 改进版本
在上述解法中,当已经能够被枚举的字符串无效时仍然继续枚举下一位置,因此浪费了很多时间.例如,对n=1,当位置0上的字符被设为’)’时,无论后续如何枚举,由这个’)’开始的字符串都是无效的.因此,提前退出枚举过程以减少运行时间.
1 | void generate(string &res, int n, int bal, vector<string> &ans) { |
解法3: 闭合数
将有效字符串表示为不相交子集的总和.
考虑有效括号序列S的闭包数, 至少存在index>=0, 使得S[0], S[1],…,S[2*index+1]是有效的.显然,每个括号序列都有唯一一个的闭包号.
对于每个闭合数c,起始和结束号位于索引0和2c+1.然后位于1-2\c之间的序列和位于2*c+1和2*n之间的序列都是有效序列.
1 | // 生成n对括号的有效序列 |
valid Parathesis string
Given a string containing only three types of characters: ‘(‘, ‘)’ and ‘*’, write a function to check whether this string is valid. We define the validity of a string by these rules:
- Any left parenthesis
'('
must have a corresponding right parenthesis')'
. - Any right parenthesis
')'
must have a corresponding left parenthesis'('
. - Left parenthesis
'('
must go before the corresponding right parenthesis')'
. '*'
could be treated as a single right parenthesis')'
or a single left parenthesis'('
or an empty string.- An empty string is also valid.
Example 1:
1 | Input: "()" |
Example 2:
1 | Input: "(*)" |
Example 3:
1 | Input: "(*))" |
Note:
- The string size will be in the range [1, 100].
解法 1:对每个*进行枚举
1 | class Solution { |
解法2:动态规划
dp[i][j]表示字符串i到j位置上的子串是否有效.
若i>j为空串, dp[i][j]=true
若i == j, dp[i][j]是否有效取决于字符串i位置上的字符是否为*, 即dp[i][j] = (s[i]==’‘)
若i+1==j,则dp[i][j]会在四种情况下有效:
s[i] == ‘(‘ && s[j] == ‘)’
s[i] == ‘(‘ && s[j] == ‘\‘ s[i] == ‘*‘ && s[j] == ‘)’
s[i] == ‘*‘ && s[j] == ‘*‘ 其余情况下为false
若i+1<j
dp[i][j] = true只在一种情况下成立,即存在i<k<=j, s[i]和s[k]满足上述四种情况之一,且dp[i][k]=true && dp[k+1][j]=true
1 | bool checkValidString(string s) { |
复杂度分析
- 时间复杂度:O(n3)
- 空间复杂度:O(n2)
解法3: 贪心
lo,hi分别表示尚未匹配的左括号的最少和最大数量.尚未匹配的左括号的最少数量在仅把左括号当做左括号时取得.最大数量在把所有*都当做右括号时取得.
- 当hi<0时,说明即使把所有*都当做左括号也无法满足左括号和右括号之间的匹配关系,因此,此字符串无效.
- 当hi>0, 而lo<0时,说明将部分*当做左括号才能满足左括号和右括号之间的匹配关系.
- 当hi>0, 而lo>0时, 说明左括号的数量多于右括号的数量.尚未匹配的左括号的最少数量还是lo.
- 当hi=0,lo=0时,说明左括号和右括号的数量相等.
1 | bool checkValidString(string s) { |
Longest valid parathesis
解法1:暴力破解
列举从每个位置开始的子串能够获得的最大长度.若某位置的字符是’)’,则从该位置获取的最大子串长度为0。
1 | Example: |
1 | class Solution { |
超时。
解法2:动态规划
使用数组dp,dp[i]表示以索引i处的元素结尾的子串中是合法括号串的串的长度。
如果s[i] = ‘(‘,那么以s[i]结尾的子串中是合法括号串的长度为0。因为合法的括号串不会以’(‘结尾。具体过程参考这里
如果s[i] = ‘)’,
- 如果s[i-1] = ‘(‘,那么dp[i] = dp[i-2] + 2
- 如果s[i-1] = ‘)’,并且s[i - dp[i-1] - 1] = ‘(‘,那么
dp[i] = dp[i - 1] + dp[i - dp[i-1] - 2] + 2
1 | class Solution { |
时间复杂度:O(n)
空间复杂度:O(n)
解法3:使用栈
与其找出每个可能的子串并检查它的正确性,我们可以利用栈来
Score of Parentheses
如何区分以位置j结尾的子问题和以位置开始的子问题
Given a balanced parentheses string S
, compute the score of the string based on the following rule:
()
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.
Example 1:
1 | Input: "()" |
Example 2:
1 | Input: "(())" |
Example 3:
1 | Input: "()()" |
Example 4:
1 | Input: "(()(()))" |
Note:
S
is a balanced parentheses string, containing only(
and)
.2 <= S.length <= 50
解法1:分而治之
首先把整个字符串S分成多个子串s1, s2, …,sn.score(S) = score(s1)+score(s1)+…+score(sn)
接下来,计算每个子串si>的分数,子串si有两种可能:
- 长度为2: score(si)=1
- 长度大于2:score(si) = 2 * score(si[1:n-1])(假设n为si.size())
重点是从位置i到位置j中间的某个位置k,能够形成一个子串,这个子串是一个valid string
1 | class Solution { |