题目
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”
示例 2:
输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”
来源:力扣(LeetCode)
解题思路
way一:暴力法
包含有效括号的子串 其长度一定是 偶数,且 其开头应为 “(”,群举出所有以“(”开头的 偶数length的子串,然后利用 栈 的形式判断其是否为 包含有效括号的子串。
具体思路:
step1:遍历一次string s,以字典的形式记录下以“(”开头的 可能的偶数length的字符串,其中,key为"(",value为各个可能的 偶数length的 string;
step2:遍历所有可能的string,以栈的形式判断其是否为 合法的 括号string,如果是,则将其与当前的 max_length 进行比较,如果 > max_length,更新 max_length,以及对应的string value;
step3:将最终的 max_length 和 string 以字典的形式返回;
#python
def longest_sub_string(s):
length = len(s)
sub_string_list = list()
for i in range(length):
if(s[i] == "("):
remain_length = length - i
if(remain_length%2 == 1):
remain_length = remain_length - 1
sub_string_num = remain_length/2
j = 1
for k in range(sub_string_num):
j = j*2
sub_string_list.append(s[i:j+i])
#通过上述步骤可以搜集所有可能的 string
#接下来,利用栈的方式来判断各个substring是否合法,判断方法为:如果为),且栈顶元素为(,则两个同时出栈,到输入结束时,如果栈内元素为0,则说明合法,记录其长度,并与max_length比较;
stack = []
front = -1
rear = 0
num_of_string = len(sub_string_list)
max_length = 0
longest_string = ""
for i in range(num_of_string):
l = len(sub_string_list[i])
for j in range(l):
if(sub_string_list[i][j] == ")" and stack[front] == "("):
front = front - 1
else:
stack[++front] = sub_string_list[i][j]
if(front == -1):
string_l = len(sub_string_list[i])
if(string_l > max_length):
max_length = string_l
longest_string = sub_string_list[i]
return (max_length,longest_string)
分析:时间复杂度为O(n2),空间复杂度为O(n3);
改进方法:可以直接用两层for循环来确定所有 substring:
for(i=0;i<s.length();++i){
for(j=i+2;j<s.length();j+=2){
substring = string[i:j]
}
}
在得到substring后,直接判断其是否合法(可以写一个函数判断);
利用这种方法,空间复杂度降为了O(n),其主要用于 栈空间;
way二:动态规划法
一个 合法substring的最后一位肯定是 “)”,根据这一定律,我们可以总结出以下规律:
当s[i]=“(“时,dp[i]=0; //这里dp[i]指的是以s[i]为尾的 substring的长度;
当s[i]=”)“时,如果s[i-1]==”(”,dp[i] = dp[i-2]+2;
当s[i]=“)“时,如果s[i-1]==”)”,那么,如果s[i-dp[i-1]-1]==“(”,则,dp[i] = dp[i-1]+dp[i-dp[i-1]-2]+2;如果s[i-dp[i-1]-1]==“)”,则dp[i]=0;
根据这一规则,通过 备忘录dp,来求解 每个结尾下的最大string长度,并且实时更新 max_length;
#python
def longest_sub_string(s):
length = len(s)
dp = []
max_length = 0
for i in range(length):
if(s[i] == "("):
dp[i] = 0
elif(s[i] == ")"):
if(s[i-1] == "("):
dp[i] = dp[i-2] + 2 #在实际实现中,要考虑i和list的关系,不可越界;i最开始可以从2开始;
elif(s[i-1] == ")"):
if(s[i-dp[i-1]-1] == "("):
dp[i] = dp[i-1]+dp[i-dp[i-1]-2]+2
else:
dp[i] = 0
if(dp[i] > max_length):
max_length = dp[i]
分析:时间复杂度为O(n),空间复杂度为O(n);
way三:栈
依次将string s的各个元素入栈,如果stack[front] == “(”,即将入栈元素为")",则出栈stack[front],并且 计算substring长度,更新max_length;
#python
def longest_sub_string(s):
max_length = 0
length = len(s)
stack = []
front = -1
rear = 0
for i in range(length):
if(s[i] == ")" and stack[front] == "("):
sub_len = i - stack[front] #stack[front]中存储的是index
front = front - 1
if(sub_len > max_length):
max_length = sub_len
else:
front = front + 1
stack[front] = i
return max_length+1
分析:时间复杂度O(n),空间复杂度O(n);
way四:依然采用栈的思想,只是用left和right两个变量表示栈的存储状况,从而将 空间复杂度从O(n)降至O(1)
#python
def longest_sub_string(s):
left = 0 #用left记录"("的个数
right = 0 #用right记录")"的个数
#如果right > left,则string不合法
max_length = 0
length = len(s)
#正向遍历
for i in range(length):
if(s[i] == "("):
left = left + 1
else:
right = right + 1
if(right == left):
sub_len = 2 * left
if(sub_len > max_length):
max_length = sub_len
elif(right > left):
left = 0
right = 0
else:
pass
#逆向遍历
left = 0
right = 0
for i in range(length):
if(s[length-i-1] == ")"):
right = right + 1
else:
left = left + 1
if(right == left):
sub_len = 2 * left
if(sub_len > max_length):
max_length = sub_len
elif(left > right):
left = 0
right = 0
else:
pass
return max_length
分析:时间复杂度为O(n),空间复杂度为O(1);
总结:因为最近一直看的都是 “动态规划” 相关的 题集,所以思维容易局限于一个方向,而忽略其他可能。
动态规划:主要就是找出 可以做memo的规律,然后以 已有的经验,去计算后序子问题的value;不一定非要递归,重点 找到memo;
推荐歌曲《渡我不渡她》
所有美好的、纯粹的、干净的、至死不渝的 感情,也只存在于 虚幻;
留白几分钟
感受纯粹、美好
…