1. 程式人生 > >[Week 10] LeetCode 32. Longest Valid Parentheses

[Week 10] LeetCode 32. Longest Valid Parentheses

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)