一. 题目
- 题目
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。 - 示例
二. 方法一: 暴力
-
解题思路
判断每一个子串是否是合法的, 然后返回最长合法子串的长度
python 运行会超时 -
解题代码
def isValid(self, s:str): list1 = [] for ele in s: if ele == '(': list1.append(ele) elif list1 and list1[-1] == '(': list1.pop() else: return False return True if not list1 else False def longestValidParentheses(self, s: str) -> int: size = len(s) max_len = 0 for i in range(0, size): for j in range(i + 1, size +1): if self.isValid(s[i: j]): if max_len < j - i: max_len = j - i return max_len
-
分析:
时间复杂度: O(n^3)
空间复杂度: O(n)
三. 方法二: 栈
-
解题思路
之前是将"(“括号放入列表, 遇到”)“就弹出, 每弹出一次就将长度+2, 如果遇到括号不匹配, 则将当前的长度修改为0,
然后继续判断后续的括号 存在的问题: 如果存在多余的”(", 不能判断出来, 如"()(()"- 将"("的下标存入到列表中,
- 如果遇到")", 则弹出末尾的元素
- 如果弹出元素后, 列表为空, 则将当前的下标存入到列表
- 如果弹出后不为空, 则获取当前最长括号的有效长度: 当前索引 - 末尾最后一个元素的值
- 返回最大值
-
解题代码
-
错解
def longestValidParentheses(s: str) -> int: max1 = 0 len1 = 0 list1 = [] for ele in s: if ele == '(': list1.append(ele) elif list1 and ele == ')' and list1[-1] == '(': list1.pop() len1 += 2 if max1 < len1: max1 = len1 else: len1 = 0 return max1
-
正解:
def longestValidParentheses(self, s: str) -> int: max1 = 0 list1 = [-1] for i in range(len(s)): # 如果当前元素是"(", 则将下标索引存入到列表末尾 if s[i] == '(': list1.append(i) else: # 如果当前元素是")", 则将列表最后一个元素弹出 list1.pop() # 如果此时列表为空, 则存放当前元素的下标到列表中 if not list1: list1.append(i) else: # 获取列表中最后一个元素的值 # 即: 当前合格符号的起始下标 ite = list1[-1] # i - list1[-1] 表示当前合格符号的长度 max1 = max(max1, i - list1[-1]) return max1
-
-
分析:
时间复杂度: O(n)
空间复杂度: O(n)
四. 方法三: 动态规划
-
解题思路
-
解题代码
def longestValidParentheses(s: str) -> int: size = len(s) # 创建一个数组 # list1[i]表示以下标为i的字符为结尾的最长有效括号的子串的长度 list1 = [0 for _ in range(size)] max_len = 0 for i in range(1, size): # 如果当前位置是")" if s[i] == ')': # 如果前一个位置是"(", 此时刚好可以组合成"()" # "XXX?()" if s[i - 1] == '(': # 如果 i < 2, 则此时直接赋值为2, 相当于字符串以"()"开头 list1[i] = 2 # 如果 i >= 2, 则此时直接在原有值的基础上加2 if i - 2 >= 0: list1[i] = list1[i - 2] + 2 # 如果当前位置的前一个元素也是")" # "?((...))" elif list1[i - 1] > 0: # 获取到list1中起一个元素的最长合法符号的起始下标 # 当前下标 - 前一个元素最长合法符号的长度 pre = i - list1[i - 1] # 如果前一个元素还是为"(", 相当于将之前的子串用"(" 和 ")"包起来了 if pre - 1 >= 0 and s[pre - 1] == '(': # 在原有的长度基础上 + 2 list1[i] = list1[i - 1] + 2 # 如果拼接后之前的长度还是 >= 2 # "(...)((...))" if pre - 2 >= 0: # 则在现有的基础上 还要加上之前的最长合法符号的长度 list1[i] += list1[pre - 2] max_len = max(max_len, list1[i]) return max_len
-
分析
时间复杂度: O(n)
空间复杂度: O(n)
五. 方法四: 不需额外的空间
-
解题思路
- 两个计数器 leftft 和 right
- 首先,我们从左到右遍历字符串,
- 对于遇到的每个 ‘(’,我们增加 left计算器; 对于遇到的每个 ‘)’ ,我们增加 right 计数器。
- 每当 left 计数器与 right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。
- 如果 right 计数器比 left 计数器大时,我们将 left 和 right 计数器同时变回 0 。
- 对于遇到的每个 ‘(’,我们增加 left计算器; 对于遇到的每个 ‘)’ ,我们增加 right 计数器。
- 然后,我们从右到左遍历字符串,
- 每当 left 计数器与 right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。
- 如果 left 计数器比 right 计数器大时,我们将 left 和 right 计数器同时变回 0 。
- 返回最长长度即可
-
解题代码
def longestValidParentheses(self, s: str) -> int: size = len(s) left, right = 0, 0 max_len = 0 # 第一轮循环: 从左到右 for i in range(size): if s[i] == '(': left += 1 if s[i] == ')': right += 1 if left == right: # 计算当前有效字符串的长度 len1 = 2 * left max_len = max(max_len, len1) # ")" 比 "("多 if left < right: left, right = 0, 0 left, right = 0, 0 # 第二轮循环: 从右到左 for i in range(size - 1, -1, -1): if s[i] == '(': left += 1 if s[i] == ')': right += 1 if left == right: # 计算当前有效字符串的长度 len1 = 2 * left max_len = max(max_len, len1) # "(" 比 ")"多 if left > right: left, right = 0, 0 return max_len
-
分析
时间复杂度: O(n)
空间复杂度: O(1), 就创建了几个变量
六. 方法五: 状态标记
-
解题思路
此法是之前栈方法的一种变形, 是在评论区看到的一种解法, 我认为对开拓思维有一定的帮助, 故放在此处. -
解题放啊
def longestValidParentheses(s: str) -> int: size = len(s) # 存放所有"("的下标 list1 = [] max_len = 0 len1 = 0 # 标记出无法匹配的括号 mark = [0 for _ in range(size)] for i in range(size): if s[i] == '(': list1.append(i) else: # 如果此时列表为空, 则")"无法匹配, 所以标记为1 if not list1: mark[i] = 1 else: # 否则, 弹出与之相匹配的"(" list1.pop() # 此时list1中存放的元素, 即为未匹配的多余的"(", 标记为1 while list1: mark[list1[-1]] = 1 list1.pop() # 然后遍历mark获取所有的状态, 最长的连续0的长度即为最长有效括号的长度 for i in range(size): # 如果遇到了不匹配的括号, 直接修改当前长度为0 if mark[i]: len1 = 0 continue len1 += 1 max_len = max(max_len, len1) return max_len
-
分析
时间复杂度: O(n)
空间复杂度: O(n)