LeetCode刷题:32. Longest Valid Parentheses

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/seagal890/article/details/89166550

LeetCode刷题:32. Longest Valid Parentheses

原题链接:https://leetcode.com/problems/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数组)对应原始序列各个子序列的解,即使用dp数组保存以位置 i 为结尾的最长符合规则序列的长度。于是这个dp数组有以下递推规则:

  • s[i] == '(',则以此为结尾的序列必然没有满足条件的子序列,故 dp[i] = 0
  • s[i] == ')',那么首先需要进行判断:
  1. dp[i-1] == 0,此时也有两种情况:

         (1) s[i-1] == ')',此时前 i-1 个字符应该形如 ...),且这个)不能与之前的组成配对,那么必有 i 位置处的)也不能完成配对,故此时 dp[i] = 0

        (2) s[i-1] == '(',那么 s[i] 和 s[i-1] 刚好组成一对,于是有 dp[i] = dp[i-2]

     2. dp[i-1] != 0,此时前 i 个字符形如AB),且B是符合规则的序列,而要判断此时加上一个)是否仍旧符合规则,则需要判断A部分的最后一个字符是否是(,当其为(时,加上)仍旧是符合规则的,即需要先判断 s[i - dp[i-1] - 1] == '(' 是否成立,如果成立的话,则还应该判断A部分去掉最后的(之后是否还有符合规则的子序列,有 dp[i] = 2 + dp[i-1] + dp[i - dp[i-1] - 2]

算法设计:

package com.bean.algorithm.dp;

public class LongestValidParentheses {
	
	public static int lvp(String s) {
	    char[] cs = s.toCharArray();
	    int longest = 0;
	    int[] dp = new int[cs.length];
	    for (int i = 1; i < cs.length; i++) {
	        if (cs[i] == ')') {
	            if (cs[i-1] == '(') {
	            	dp[i] = (i >= 2 ? dp[i-2] : 0) + 2;
	            } else if (i - dp[i-1] > 0 && cs[i - dp[i-1] - 1] == '(') {
	            	dp[i] = dp[i-1] + ((i - dp[i-1]) >= 2 ? dp[i - dp[i-1] - 2] : 0) + 2;
	            }
	            longest = longest > dp[i] ? longest : dp[i];
	        }
	    }
	    return longest;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String str=")()())";
		int result=lvp(str);
		System.out.println("result = "+result);
	}

}

运行结果:

result = 4

看到另外一种解法:

基本的想法就是用栈来实现。从字符串的第一个位置开始读起,遇到左括号的话就入栈,遇到右括号的话就将栈顶元素弹出,并且判断当前序列的最长长度。栈里保存的是左括号的位置。 
具体遇到右括号时,有如下几种情况需要考虑:

当前栈内无元素,则用start变量记录当前右括号的位置,表明下次比较从该右括号下一个位置开始判断;

当前栈内有元素,则将栈顶元素弹出。如果弹出后的栈为空,则表明当前括号匹配,这时计算出当前的最长序列长度,即当前位置的值减去start的值即是;

当前栈内又元素且弹出后的栈内仍然有元素,则最长序列长度为当前元素位置减去栈顶元素的位置。

算法设计

	 /*
     * 这道题的思想就是使用栈遍历一次思想对打有效括号的匹配。
     * 从字符串的第一个位置开始读起,遇到左括号的话就入栈,遇到右括号的话就将栈顶元素弹出,并且判断当前序列的最长长度。
     * 栈里保存的是左括号的位置。
     * 
     * 具体遇到右括号时,有如下几种情况需要考虑:
     * 1. 当前栈内无元素,则用start变量记录当前右括号的位置,
     * 表明下次比较从该右括号下一个位置开始判断;
     * 
     * 2. 当前栈内有元素,则将栈顶元素弹出。如果弹出后的栈为空,
     * 则表明当前括号匹配,这时计算出当前的最长序列长度,即当前位置的值减去start的值即是;
     * 
     * 3. 当前栈内又元素且弹出后的栈内仍然有元素,
     * 则最长序列长度为当前元素位置减去栈顶元素的位置。
     * 
     * 本算法仅需要扫描一遍字符串,所以时间复杂度为O(n);
     * 
     * */
    public static int longestValidParentheses(String s) 
    {
        if(s==null || s.length()<=0)
            return 0;

        int beginIndex=0,maxLen=0;
        Stack<Integer> stack=new Stack<>();
        for(int i=0;i<s.length();i++)
        {
            //遇到 ( 均压入栈,压入的是index
            if(s.charAt(i)=='(')
                stack.push(i);
            else  //  ')'
            {
                //遇到空,说明当前无法构成合理的括号配对
                if(stack.isEmpty())
                    beginIndex=i+1;
                else
                {
                    stack.pop();
                    if(stack.isEmpty())
                        maxLen=Math.max(maxLen, i-beginIndex+1);
                    else
                        maxLen=Math.max(maxLen, i-(stack.peek()+1)+1);
                }
            }
        }
        return maxLen;
    }

运行结果:

result = 4

猜你喜欢

转载自blog.csdn.net/seagal890/article/details/89166550