0032. Longest Valid Parentheses (H)

Longest Valid Parentheses (H)

题目

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: 2
Explanation: The longest valid parentheses substring is "()"

题意

给定一个只包含左右括号的字符串,输出连续匹配括号对的最长长度。

思路

个人想到的方法是压栈法:建两个栈,一个par存括号字符,另一个index存括号字符对应的下标。用i遍历字符串,如果为'(',则同时压入两个栈;若为')',此时如果par为空,则压入par和index,如果par非空且栈顶元素为'(',说明配对成功,同时出栈par和index,而此时index的栈顶元素(非空时)代表的是当前最右边无法匹配的括号的下标,那么 i - index.peek() (index非空)/ i + 1 (index为空) 就代表了新增一对匹配括号对后更新的连续匹配长度,将之与max比较并更新max。
官方的压栈法解答只使用了一个栈,但整体思路是一样的。

左右扫描法:设两个计数器left和right,先从左向右扫描字符串,'('则left+1,')'则right+1,每扫描一个字符串,比较left和right,如果left < right,则将left和right重置,如果left == right,则更新max;同理再从右向左扫描,如果left > right,则将left和right重置,如果left == right,则更新max。
这是因为左右扫描,不能正确处理"( ) ( ( ( ) )"这种情况,而右左扫描可以;右左扫描不能正确处理"( ( ) ) ) ( )"这种情况,但左右扫描可以。

动态规划:dp数组记录以当前括号字符为结尾的最大有效长度,显而易见只有以')'为结尾才可能形成有效字符串,因此只有在当前字符为')'时才进行处理,共可能会有以下两种情况:

  1. s[i] == ')' && s[i - 1] == '(',这时很容易得到 \(dp[i] = dp[i - 2] + 2\)
  2. s[i] == ')' && s[i - 1] == ')',这种情况下,先找到以s[i - 1]为结尾的有效字符串开头的前一个字符c (当然如果dp[i - 1] == 0,就不需要找这个c了),如果c为'(',则与s[i]构成匹配括号对,同时还要考虑以c前一个字符c'为结尾的有效字符串的长度 (举个例子,"( ) ( ( ) )",最后一个')'为s[i]),最终得到 \(dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2]\)

代码实现

Java

压栈法

class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;

        Deque<Character> par = new ArrayDeque<>();
        Deque<Integer> index = new ArrayDeque<>();

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == '(') {
                par.push(c);
                index.push(i);
            } else {
                if (par.isEmpty() || par.peek() != '(') {
                    par.push(c);
                    index.push(i);
                } else {
                    par.pop();
                    index.pop();
                    max = Math.max(max, index.isEmpty() ? i + 1 : i - index.peek());
                }
            }
        }

        return max;
    }
}
class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;

        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(-1);

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == '(') {
                stack.push(i);
            } else {
                stack.pop();
                // 出栈后若栈空,说明出栈的是不匹配的')',将当前')'压入栈
                // 若栈非空,说明出栈的是匹配的'(',更新max
                if (stack.isEmpty()) {
                    stack.push(i);
                } else {
                    max = Math.max(max, i - stack.peek());
                }
            }
        }
        
        return max;
    }
}

左右扫描法

class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;
        int left = 0, right = 0;

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                max = Math.max(max, left + right);
            } else if (left < right) {
                left = right = 0;
            }
        }

        left = right = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
            char c = s.charAt(i);
            if (c == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                max = Math.max(max, left + right);
            } else if (left > right) {
                left = right = 0;
            }
        }
        
        return max;
    }
}

动态规划

class Solution {
    public int longestValidParentheses(String s) {
        int max = 0;

        int[] dp = new int[s.length()];
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == ')') {
                if (s.charAt(i - 1) == '(') {
                    dp[i] = i - 2 >= 0 ? dp[i - 2] + 2 : 2;
                } else {
                    if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                        dp[i] = dp[i - 1] + 2 + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0);
                    }
                }
            }
        }
        
        for (int i = 0; i < s.length(); i++) {
            max = Math.max(max, dp[i]);
        }

        return max;
    }
}

JavaScript

压栈法

/**
 * @param {string} s
 * @return {number}
 */
var longestValidParentheses = function (s) {
  let max = 0
  let stack = []
  for (let i = 0; i < s.length; i++) {
    if (s[i] === '(' || stack.length === 0 || s[stack[stack.length - 1]] === ')') {
      stack.push(i)
    } else {
      stack.pop()
      max = Math.max(max, i - (stack.length === 0 ? -1 : stack[stack.length - 1]))
    }
  }
  return max
}

左右扫描法

/**
 * @param {string} s
 * @return {number}
 */
var longestValidParentheses = function (s) {
  return Math.max(scan(s.split(''), true), scan(s.split('').reverse(), false))
}

let scan = function (s, flag) {
  let left = 0, right = 0
  let max = 0
  for (let p of s) {
    if (p === '(') {
      left++
    } else {
      right++
    }
    if (left === right) {
      max = Math.max(max, left + right)
    } else if (flag ? left < right : left > right) {
      left = right = 0
    }
  }
  return max
}

动态规划

/**
 * @param {string} s
 * @return {number}
 */
var longestValidParentheses = function (s) {
  let max = 0
  let dp = new Array(s.length).fill(0)

  for (let i = 1; i < s.length; i++) {
    if (s[i] === ')') {
      if (s[i - 1] === '(') {
        dp[i] = i > 1 ? dp[i - 2] + 2 : 2
      } else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] === '(') {
        dp[i] = dp[i - 1] + 2 + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0)
      }
      max = Math.max(max, dp[i])
    }
  }

  return max
}
posted @ 2020-06-25 04:03  墨云黑  阅读(107)  评论(0编辑  收藏  举报