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] == ')',那么首先需要进行判断:
- 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