LeetCode-32. Longest Valid Parentheses

前言

用了一个小时,想了一种O(n)的算法,结果发现才超过百分之十几的人。。。但是后来看了solution,基本也都是这种复杂度呀,为啥我的运行的这么慢呢?可能是代码写的太烂了吧,又参考了别人的思想,写了动态规划的解法,如果有时间就写下来,大家也可以去看官方给的解法。

问题描述

在这里插入图片描述

解法一

下面的解法就是我的垃圾解法,首先通过一个list来实现堆栈,对整个数组进行遍历,找到所有可以配对的括号,记录在另一个list中,然后再遍历另一个list,找到连续为1的最长的段就是结果啦!

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if s == None:
            return
        stac = []
        count = 0
        ce = [0]*len(s)
        for k in s:
            if len(stac) == 0 :
                stac.append([count,k])
            else :
                if stac[-1][1]=='(' and k == ')':
                    ce[stac[-1][0]] = 1 #如果能配对则标记为1
                    ce[count] = 1
                    stac.pop()
                else:
                    stac.append([count,k])
            count +=1
            
        maxlen = 0
        curr = 0
        for k in ce:
            if k == 1:
                curr +=1#如果是1则长度加1 
            else:
                if curr>maxlen:#否则检查是否是最长的
                    maxlen = curr
                curr = 0#长度置零 重新找连续的长度
                
        if curr>maxlen:#有可能最后一个也是符合条件的 
            maxlen = curr
            
        return maxlen
                    

如果没有看明白,那就看一下ce 的输出值就明白我的意思了:
在这里插入图片描述
这个解法就是遍历两遍字符串,时间复杂度和空间复杂度都是O(n),如果是c语言之类的,标志位可以直接赋值在原字符串中,而不用重新申请一个list了。。。但是运行速度就是慢,可能是中间有很多的判断吧。。。

解法二

DP解法,说实话,这个题看到它的第一眼我就想到了动态规划,但是我尝试规划失败了(-_-),感觉分析的有点乱,一直没有弄清楚其中的关系。直到看到了官方给的DP算法,才算大致明白如何规划了吧。这里就说一下我简单的理解吧!
首先定义d(n)代表以第n个字符结尾的最长字符串的长度,因为只有 ‘)’ ,才算是合法的结尾,而 ‘(’ 结尾的最长字符串长度应该是零。所以先假设d全部为零,然后逐个加入字符。
在已知d(n-1)的情况下,求第n个字符加入进来后当前字符串的最长长度。
下面讨论几种情况:
如果s(n)=’(’:不用讨论,d(n) = 0(默认)
否则:
1>s(n)=’)’ 并且 s(n-1) = '('
此时最后两个字符构成一个合法的括号,则以当前字符s(n)结尾最长的长度为d(n-2)+2
2>s(n)=’)’ 并且 s(n-1) = ')'
这个时候如果s(n)=’)’ 为结尾构成了一个合法结尾,那么在前面必须有一个 ‘(’ 与之对应,而且这个 '('的位置肯定得在n-d(n-1)-1的位置上,如下图所示:

在这里插入图片描述
那么此时以s(n)=’)’ 为结尾的d(n) = d(n-d(n-1)-2) + 1 + d(n-1) +1 = d(n-d(n-1)-2) + d(n-1) + 2
由此全部情况都讨论完了,中间写代码时需要注意初始值的判断即可,我的代码如下,里面有些注释,看不懂的可以问我!

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if s == None:
            return
        maxlen = 0
        d = [0]*len(s)
        for k in range(1,len(s),1):
            if s[k] == ')':#只处理最后一个字符是')',因为只有这样才会是一个合法的结尾
                if s[k-1] == '(':#如果是()则加2
                    if k>=2:   
                        d[k] = d[k-2]+2
                    else:
                        d[k] = 2
                else:#如果是)) 则要判断d[s-1]个字符前是否是'('
                    if k>d[k-1] and s[k-1-d[k-1]]=='(':#如果是 则说明从第k-d[s-1]-1到最后一个构成合法字符串
                        if k-d[k-1]-2>=0:
                            d[k]= d[k-d[k-1]-2] + d[k-1] + 2 # 长度二是[k-d[s-1]-1,k]这两个字符
                        else:
                            d[k] = d[k-1] + 2
                if d[k]>maxlen:
                    maxlen = d[k]
            
        return maxlen

这个算法一下子超过了99.95%的人,哇咔咔,看来O(n)和O(n)还是有一定的差距的 ! 学习到了。。

解法三

这个解法是看的官方给的代码,类似于双指针,先从前往后,累计计算左括号和右括号的数量。
如果两者相等则说明此时构成了合法子串,判断其长度(等于左右括号的数量)是否大于最大长度,如果大,则更新。
如果出现右括号数量比左括号数量多的,则说明出现了非法的子串,此时重新开始计数左括号和右括号数量,直到遍历整个字符串。
最后再从后往前遍历一遍,找到最长的长度值即可。
下面给出代码实现,实际上就是官方的代码实现,而且还是java实现的,我就不改成python版本的了,直接拿来用了,这里给出代码链接

public class Solution {
    public int longestValidParentheses(String s) {
        int left = 0, right = 0, maxlength = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                left++;//如果找打左括号 左括号计数加
            } else {
                right++;//右括号计数加
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * right);//更新最大长度值
            } else if (right >= left) {
                left = right = 0;//重新开始计数
            }
        }
        //倒着重新遍历一遍
        left = right = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * left);
            } else if (left >= right) {
                left = right = 0;
            }
        }
        return maxlength;
    }
}

总结

唉,做题用了一个小时,写这个博客用了近两个小时,虽然很费时,虽然网上也有很多人写这样的博客,虽然这样很像造轮子,但是这样真的对题目会有更多的理解,也算是一种提升吧,不能只闷头刷题,要适当的有点总结!希望自己能够坚持下去吧!
最后送上我比较喜欢的一句话,共同勉励!
在这里插入图片描述

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值