[Week 10] LeetCode 32. Longest Valid Parentheses

15 篇文章 0 订阅
13 篇文章 0 订阅

LeetCode 32. Longest Valid Parentheses

问题描述

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 "()()"

题解

题目的意思很简单,就是找到最长的子串满足:'('')' 配对。

看起来跟那些寻找最长子串、和最大子串的题目差不多,于是我们可以尝试着用 DP 去解决它,首先我们看看怎样的子串可以满足要求:

  1. "()""()()"
  2. "(())""(()())"

总的来说,分为三类:单独成对、嵌套成对以及两者混合的。

那我们可以开始思考怎么去解决这个问题了,从头扫描输入串,并用 count 数组来记录以输入串同一索引所指元素作为子串最后一个字符的匹配子串长度 s,用 i 表示当前扫描索引:

  • s[i] == '(' 就将其索引压入栈(记住是索引,因为我们只把 '(' 压入栈),同时 count[i] = count[i - 1]
  • s[i] == ')'
    • 如果栈非空,则弹出栈顶元素,同时 count[i] = count[i - 1] + 2
    • 否则,count[i] = 0

解释一下:

  • s[i] == '(' 时,我们并不知道它后面能不能构成匹配的子串,不妨假设它可以,我们可以连着前面字符构成一个子串;
  • s[i] == ')' 时,我们可以通过栈是否为空来判断其是否能构成匹配的子串,如果可以,理所当然在前面的子串基础上填加了一对匹配的括号 ;如果不可以,当然是将 count[i] 置 0,因为它将串给隔开了。

当然做到这步还不足够的!因为考虑这样的一个输入串:

s:       ()(()
count:   02224
correct: 02002

很容易想到哪里出了问题:当 s[i] == '(' 时我们是假设了它可以构成匹配的子串,当在上面的例子中 s[2] 这个 ‘(’ 是不能构成匹配子串的。我们已经做到这一步了,能不能补救一下呢?

答案是可以的!还记得前面我们的栈压入的是索引吗?当我们扫描完,还存留在栈中的它们是不能成功匹配的 ')' 的索引。前面的问题就是我们没有考虑 '(' 不是成功匹配子串一员导致 count[i] 加多了前面那一段的长度,所以减掉就行了:

# stack: 2
  s:  count:  
0 (     0
1 )     2
2 (     2 - count[2] = 0
3 (     2 - count[2] = 0
4 )     4 - count[2] = 2

一个包含多个未成功匹配的 '(' 例子:

# stack: 2,5,6,7
  s:  count: 
0 (     0
1 )     2
2 (     2 - count[2] = 0
3 (     2 - count[2] = 0
4 )     4 - count[2] = 2
5 (     4 - count[5] = 0
6 (     4 - count[6] = 0
7 (     4 - count[7] = 0

Code

class Solution {
public:
    int longestValidParentheses(string s)
    {
      if (s.size() == 0)
        return 0;

      vector<int> count(s.size(), 0);
      stack<int> st;

      for (int i = 0; i < s.size(); ++i)
      {
        if (s[i] == '(')
        {
          st.push(i);
          count[i] = count[i - 1];
        }
        else
        {
          // 不匹配
          if (st.empty())
            count[i] = 0;
          // 匹配
          else
          {
            count[i] = count[i - 1] + 2;
            st.pop();
          }
        }
      }

      // 对不匹配的'('进行纠错
      int begin, end = count.size() - 1;
      while (!st.empty())
      {
        begin = st.top();
        st.pop();
        for (int i = begin + 1; i <= end; ++i)
          count[i] -= count[begin];
        count[begin] = 0;
        end = begin - 1;
      }

      // 遍历 count 数组找出最大值
      int maxCount = count[0];
      for (auto i : count)
        if (maxCount < i)
          maxCount = i;

      return maxCount;
    }
};

复杂度

算法的复杂度一眼就可以看出来啦,我们正向扫描了输入串,以及在纠错过程中至多扫描一次输入串,所以复杂度为 O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值