📉
leetcode-题解
  • leetcode-notes
  • linked-list
    • 2-Add-Two-Numbers
    • 109-Convert-Sorted-List-to-Binary-Search-Tree
    • 19-Remove-Nth-Node-From-End-of-List
    • 92-Reverse-Linked-List-II
    • 142-Linked-List-Cycle-II
    • 83-Remove-Duplicates-from-Sorted-List
    • 61-Rotate-List
    • 148-Sort-List
    • 86-Partition-List
    • 82-Remove-Duplicates-from-Sorted-List-II
    • 138-Copy-List-with-Random-Pointer
    • 328-Odd-Even-Linked-List
    • 23- Merge-k-Sorted-Lists
    • 25-Reverse-Nodes-in-k-Group
  • templates
    • bitmap
    • ologn
    • Morris
    • dp
    • binary-search
    • Maxwindow
    • 递归
    • union
    • graph
    • greedy-algorithm
    • kmp
    • list
    • ordered-list
    • tree
    • Manacher
    • Monotonic-stack
    • big-data
    • sort-Summary
    • Bucket-sort
    • bit-opreation
    • heap-sort
  • arrays
    • others
      • 31-Next-Permutation
      • 66-Plus- One
      • 229-Majority-Element-II
      • 414-Third-Maximum-Number
    • matrix
      • 74-Search-a-2D-Matrix
      • 289-Game-of-Life
    • PrefixOrSuffix
      • 560-Subarray-Sum-Equals-K
      • 238-Product-of-Array-Except-Self
    • 二分法
      • rotated-array-problem
      • D天内送达包裹的能力
      • 162-Find-Peak-Element
      • Minimize-maximum-and-maximize-minimum
    • 多指针
      • 611-Valid-Triangle-Number
      • 228-Summary-Ranges
      • 75-Sort-Colors
      • 18-4Sum
      • 27-Remove-Element
      • 三数之和
      • 26-Remove-Duplicates-from-Sorted-Array
      • 盛最多水的容器
      • 80-Remove-Duplicates-from-Sorted-Array-II
      • 最接近的三数之和
    • array-circle
      • 457-Circular-Array-Loop
      • 287-Find-the-Duplicate-Number
      • 565-Array-Nesting
    • 智力题
      • 73-Set-Matrix-Zeroes
      • 最佳观光组合
    • 几何问题
      • 统计全为1的正方形子矩阵
      • 495-Teemo-Attacking
    • sort
      • 88-Merge-Sorted-Array
      • 57-Insert-Interval
  • tree
    • 105-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal
    • 230-Kth-Smallest-Element in-a-BST
    • 106-Construct-Binary-Tree-from-Inorder-and-Postorder-Traversal
    • 257-Binary-Tree-Paths
    • 113-Path-Sum-II
    • 96-Unique-Binary-Search-Trees
    • 124-Binary-Tree-Maximum-Path-Sum
    • 103-Binary-Tree-Zigzag-Level-Order-Traversal
    • 426-Convert-Binary-Search-Tree-to-Sorted-Doubly-Linked-List
    • 117-Populating-Next-Right-Pointers-in-Each-Node-II
    • 99-Recover-Binary-Search-Tree
    • 366-Find-Leaves-of-Binary-Tree
    • 337-House-Robber-III
    • 333-Largest-BST-Subtree
    • 298-Binary-Tree-Longest-Consecutive-Sequence
    • 428-Serialize-and-Deserialize-N-ary-Tree
    • 1367-Linked-List-in-Binary-Tree
    • 173-Binary-Search-Tree-Iterator
    • 98-Validate-Binary-Search-Tree
    • 156-Binary-Tree-Upside-Down
    • 404-Sum-of-Lef- Leaves
    • 255-Verify-Preorder-Sequence-in-Binary-Search-Tree
    • 272-Closest-Binary-Search-Tree-Value-II
    • 95-Unique-Binary-Search-Trees-II
    • 222-Count-Complete-Tree-Nodes
    • 431-Encode-N-ary-Tree to-Binary-Tree
    • Lowest-Common-Ancestor-of-a-Binary-Tree
    • 129-Sum-Root-to-Leaf-Numbers
  • recursive
    • 前言
    • 39-Combination-Sum
    • 79-Word-Search
    • 04-Power-Set-LCCI
    • 前言
    • 90-Subsets-II
    • 40-Combination-Sum-II
    • 351-Android-Unlock-Patterns
  • dynamic-programming
    • 276-Paint-Fence
    • 132-Palindrome-Partitioning-II
    • 361-Bomb-Enemy
    • 62-Unique-Paths
    • 376-Wiggle-Subsequence
    • 403-Frog-Jump
    • 32-Longest-Valid-Parentheses
    • 97-Interleaving-String
    • 354-Russian-Doll-Envelopes
    • 279-Perfect-Squares
    • 304-Range-Sum-Query-2D-Immutable
    • 10-Regular-Expression-Matching
    • Paint-House-series
    • 139-Word-Break
    • Best-Time-to-Buy-and-Sell-Stock-series
    • 416-Partition-Equal-Subset-Sum
    • 300-Longest-Increasing-Subsequence
    • 91-Decode-Ways
    • Ugly-Number-series
    • 363-Max-Sum-of-Rectangle-No-Larger-Than-K
    • 368-Largest-Divisible-Subset
    • 63-Unique-Paths-II
    • 312-Burst-Balloons
    • 322-Coin-Change
    • 64-Minimum-Path-Sum
    • 140-Word-Break-II
    • 120-Triangle
    • 72-Edit-Distance
    • House-Robber-series
    • 413-Arithmetic-Slices
    • 174-Dungeon-Game
    • 87-Scramble-String
    • 44-Wildcard-Matching
    • 338-Counting-Bits
    • 152-Maximum-Product-Subarray
    • 375-Guess-Number-Higher-or-Lower-II
  • hash-table
    • 381-Insert-Delete-GetRandom-O(1) - Duplicates-allowed
    • 442-Find-All-Duplicates-in-an-Array
    • 380-Insert-Delete-GetRandom-O(1)
    • 1-Two-Sum
    • 3-Longest-Substring-Without-Repeating-Characters
    • 41-First-Missing-Positive
  • stack
    • Monotonic stack
      • 84-Larges-Rectangle-in-Histogram
      • 42-Trapping-Rain-Water
  • bit-manipulation
    • 08-Draw-Line-LCCI
  • Mysql
    • 185-Department-Top-Three-Salaries
    • 177-N-Highest-Salary
    • 178-Rank-Scores
    • 180-Consecutive-Numbers
  • greedy
    • 56-Merge-Intervals
    • 55-Jump-Game
    • 53-Maximum-Subarray
  • math
    • 357-Count-Numbers-with-Unique-Digits
    • 343-Integer-Break
    • 119-Pascal's-Triangle-II
  • string
    • Palindrome
      • 5-Longest-Palindromic-Substring
      • Manacher
  • sliding-window
    • 209-Minimum-Size-Subarray-Sum
Powered by GitBook
On this page
  • 0x0 题目详情
  • 0x1 解题思路
  • 栈方法
  • 动态规划
  • 0x2 代码实现
  • 0x3 课后总结

Was this helpful?

  1. dynamic-programming

32-Longest-Valid-Parentheses

Previous403-Frog-JumpNext97-Interleaving-String

Last updated 4 years ago

Was this helpful?

0x0 题目详情

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

0x1 解题思路

栈方法

注意注意:这道题要求的是连续最长子括号的长度,一定得是连续的。求一个字符串中括号能否匹配用栈是很简单的。关键是怎么保证连续这个状态。

我刚开始是直接栈中保存左括号或者右括号了,一旦括号匹配,就将结果加2。这种做法只能求出字符串中有效括号的数量。而不是连续的括号数量。所以转而在栈中保存括号对应的下标。基本规则如下:

  • 首先要在栈中添加-1,这是为了解决()这种情况。第一组括号就直接匹配,长度为右括号下标减去栈顶中的下标。也就是1-(-1)=2

  • 如果当前字符为(,那么直接push对应的下标

  • 如果当前字符为),首先pop栈顶元素,

    • 如果栈顶为空,那么push当前下标

    • 否则更新结果:result=Math.max(result,i-stack.peekLast()。因为i是当前右括号的下标,如果弹出的是左括号,那么新栈顶就是左括号下标j的左边一个j-1,二者相减才是这组括号的长度。

从上面的规则可以看出,栈中要么保存一个)或者不保存。对于测试用例为)))),刚开始栈顶元素为-1,遇到第一个),弹出栈顶,计算的结果为0。然后栈为空,会将第二个)下标push到栈中。往后都只会保存一个(直到遍历结束。

动态规划

对于最优的策略,一定有最后一个元素 s[i]

所以,我们先看第i个位置,这个位置的元素s[i]可能有如下两种情况:

  • s[i] == '(' 这时,s[i]无法和其之前的元素组成有效的括号对,所以,dp[i] = 0

  • s[i] == ')' 这时,需要看其前面对元素来判断是否有有效括号对。

    • 情况1: s[i - 1] == (

      即s[i]和 s[i - 1]组成一对有效括号,有效括号长度新增长度2,i位置对最长有效括号长度为 其之前2个位置的最长括号长度加上当前位置新增的2,我们无需知道i-2位置对字符是否可以组成有效括号对。

      那么有:dp[i]=dp[i−2]+2

    • 情况2: s[i - 1] == ) 这种情况下,如果前面有和s[i]组成有效括号对的字符,即形如((....)),这样的话,就要求s[i-1]位置必然是有效的括号对,否则s[i]无法和前面对字符组成有效括号对。</br> 这时,我们只需要找到和s[i]配对位置,并判断其是否(即可。其配对位置为:i−dp[i−1]−1(i-dp[i-1]是s[i-1]的配对位置,再减1就是s[i]的配对位置)。

      • 如果:s[i-dp[i-1]-1] == (,那么有效括号长度新增长度2,i位置对最长有效括号长度为i-1位置的最长括号长度加上当前位置新增的2,那么有:dp[i]=dp[i−1]+2

      值得注意的是,i−dp[i−1]−1 和 i组成了有效括号对,这将是一段独立的有效括号序列,如果之前的子序列是形如 (...)这种序列,那么当前位置的最长有效括号长度还需要加上这一段。所以: dp[i]=dp[i−1] + dp[i−dp[i−1]−2]+2

      注: 这个在分析时是很容易遗漏的,分析要更细致。我在第一次分析是就遗漏了,提交后,有用例 )()(()))不过,分析后发现是少了这一段。

0x2 代码实现

栈版本:

class Solution {
    public int longestValidParentheses(String s) {
        int result=0;
        if(s==null || s.length()<2){
            return result;
        }

        LinkedList<Integer> stack=new LinkedList<>();
        stack.offerLast(-1);
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)=='('){
                stack.offerLast(i);
            }else{
                stack.pollLast();
                if(stack.isEmpty()){
                    stack.offerLast(i);
                }else{
                    result=Math.max(result,i-stack.peekLast());
                } 
            }
        }
        return result;
    }
}

动态规划版本:

class Solution {
public:
    int longestValidParentheses(string s) {
        int size = s.length();
        vector<int> dp(size, 0);

        int maxVal = 0;
        for(int i = 1; i < size; i++) {
            if (s[i] == ')') {
                if (s[i - 1] == '(') {
                    dp[i] = 2;
                    if (i - 2 >= 0) {
                        dp[i] = dp[i] + dp[i - 2];
                    }
                } else if (dp[i - 1] > 0) {
                    if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
                        dp[i] = dp[i - 1] + 2;
                        if ((i - dp[i - 1] - 2) >= 0) {
                            dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
                        }
                    }
                }
            }
            maxVal = max(maxVal, dp[i]);
        }
        return maxVal;
    }
};

0x3 课后总结

对于测试用例有连续索引的题目,如果题目要求最后的结果是连续的长度,并且可能会用到栈或队列来求。那么在栈中一般都是保存元素对应的索引,而不是真正的元素,因为这样不仅能求出长度,还能保证结果是连续的。

由于这道题我实在想不出来递归函数该怎么写,导致最后dp版本也无从下手。下面解析摘抄自:

原题链接
https://leetcode-cn.com/problems/longest-valid-parentheses/solution/dong-tai-gui-hua-si-lu-xiang-jie-c-by-zhanganan042/