LeetCode:最长有效括号

题目

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例 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;


推荐歌曲《渡我不渡她》

所有美好的、纯粹的、干净的、至死不渝的 感情,也只存在于 虚幻;

留白几分钟
感受纯粹、美好

猜你喜欢

转载自blog.csdn.net/u014765410/article/details/98475008