原题
Given a string containing just the characters ‘(’ and ‘)’, find the length of the longest valid (well-formed) parentheses substring.
题目:
给定一个只包含字符 “(’ 和 ')” 的字符串, 查找最长的有效 (格式正确) 圆括号子字符串的长度。
Example 1:
Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2:
Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"
My Solution
方案一(错误)
class Solution_four:
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
if len(s) <= 1:
return 0
res = 0
list_s = list(s)
while True:
mark = True
for i in range(1, len(list_s)):
if list_s[i - 1] == '(' and list_s[i] == ')':
res += 1
# list_s.pop(i - 1)
# list_s.pop(i)
del list_s[i], list_s[i-1]
mark = False
break
else:
pass
if mark:
break
return res
- 忽略了
()(()
情况,即只考虑了有多少个()
没有考虑有效性,所以得出上面这种情况为2,但实际应该为1; - list删除元素有三种方式,remove(), del, pop(),其中pop的参数为列表索引值,每次只能pop一个;而del直接这对list元素,后可接多个;
- 上述代码之所以将删除方式由pop转换为del,是因为
list_s.pop(i - 1)
之后,list_s的长度减1,造成list_s.pop(i)
已经不再是自己当初所设想的i-1之后的第i个元素了(因为list_s长度发生了变化),并且这种方式很容易造成列表索引超出范围(事实上,自己在这个地方卡了不少时间找原因); - 上述代码之所以在
mark = False
之后加上了break,是为了让其跳出for循环,重新让i
从1开始取值;自己之前曾尝试直接赋值i = 1
,但事实证明这种方式并不能影响for循环中i的变化,for循环的中的i
的取值不会随着for循环中list_s
长度变化而变化;(这点要尤其注意)
方案二(错误)
class Solution:
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
if len(s) <= 1:
return 0
count = count_left = count_right = 0
mark_list = ['(']
mark_start = False
left_index = {}
right_index = {}
res = 0
for i in range(1, len(list_s)):
if s[i] == '(':
count_left += 1
count += 1
mark_start = True
left_index[count_left] = i
if s[i] == ')' and mark_start:
count_right += 1
count -= 1
right_index[count_right] = i
if count_left == 0 or count_right == 0:
return 0
if count_left <= count_right:
res = s[left_index[1]:right_index[count_left]+1]
else:
res = s[left_index[count_left - count_right + 1]:right_index[count_right]+1]
return len(res)
- 试图采用之前做过的查找有效括号的做法来做,依旧是少考虑了有效性; 即忽略了
()(()
情况,即只考虑了有多少个()
没有考虑有效性,所以得出上面这种情况为2,但实际应该为1;
Reference solution
思路分析:
思路一 :
首先,这个题的总体思路相当于Valid Parentheses这道题的升级版。括号匹配,肯定会想到用栈,但是这道题不同的是要找到最长的有效括号子串,子串在寻找的过程中你无法确定它是不是有效。
例如()(()
,在当前的状态来看是两个有效括号,但是如果继续往后走,有可能是()(())
,这样的话两个有效括号就变成了一个。
因此必须能够记录匹配的长度变化,可以采用位置记录。例如()(()
,需要同时记录中间(
的位置和起始的位置0,虽然当前子串的长度判断是通过和(
位置进行差值得到的,但是一旦这个(
被匹配掉,就要和起始位置来进行比较。因此,需要一个变量start来记录有效括号的可能的最早的起始位置。
通过start来记录起始位置,
- 如果当前为
(
则将位置入栈; - 如果是
)
,则判断当前栈:- 如果当前栈为空,说明匹配到了一个无效括号,start从i+1的位置开始
- 如果当前栈不空,说明需要和栈里的
(
匹配融合掉,这时再看栈:- 如果栈为空,说明左括号全部匹配掉了,就需要用当前位置i - start + 1来更新结果值(
max(res, i - start + 1)
) - 如果栈不空,说明干掉了一个左括号,还有多余的左括号,就将最大值更新到这个左括号的位置,即
max(res, i - stack[-1] + 1)
- 如果栈为空,说明左括号全部匹配掉了,就需要用当前位置i - start + 1来更新结果值(
class Solution:
def longestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
stack = []
res = start_index = 0
for i, count in enumerate(s):
if count == '(':
stack.append(i)
else:
if not stack:
start_index = i+1 # invalid match
else:
stack.pop()
if not stack: # totally match all stack
res = max(res, i - start_index + 1)
else:
res = max(res, i - stack[-1])
return res
反思:
- 忽略了题目中有效性要求;
- 需要多反思自己答案一中出现错误的情况;
- 通过自己解决思路二,尝试运用了dict类型,明白了如何对dict类型进行元素添加:
right_index[count_right] = i
- 对于多重循环,采用
for i in range(len(s))
到内层循环会很混乱,不如采用enumerate结构,for i,count in enumerate(len(s))