题目
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:
输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:
输入:s = “”
输出:0
题解一 动态规划
class Solution {
public int longestValidParentheses(String s) {
if(s.length()==0){
return 0;
}
int[] dp=new int[s.length()];
int maxNum=0;//通过两两比较,得出dp数组中最大的数
for(int i=1;i<dp.length;i++){//i=0的时候肯定是0!
if(s.charAt(i)==')'){//(肯定为0,就不用算了
if(s.charAt(i-1)=='('){
dp[i]=(i>1?dp[i-2]:0)+2;//若i=1,则直接0+2!***
}else if((i-dp[i-1]-1)>=0&&s.charAt(i-dp[i-1]-1)=='('){//i是从0开始的,不能在索引里判断。而且这里的判断语句就有问题!i<=1了!应该把这个判断语句放到里面写!
//i-dp[i-1]>0只是为了保证charAt存在!
dp[i]=dp[i-1]+((i-dp[i-1]-2)>=0?dp[i-dp[i-1]-2]:0)+2;
}
}
maxNum=Math.max(dp[i],maxNum);
}
return maxNum;
}
}
笔记:
- 动态规划题目分析的 4 个步骤:
- 确定状态
- 研究最优策略的最后一步
- 化为子问题
- 转移方程
- 根据子问题定义得到
- 初始条件和边界情况
- 计算顺序
- 确定状态
- 最优策略的最后一步:
i−dp[i−1]−1:i减掉中间匹配上的对数
考虑最后一个元素s[i]的情况:
-
s[i]=’(’:dp[i]=0
-
s[i]=’)’:
- s[i-1]=’(’:dp[i]=dp[i-2]+2
- 形如()((…)):dp[i]=dp[i-1]+dp[i-dp[i-1]-2]+2
dp[i-1]:里层小括号的匹配数;dp[i-dp[i-1]-2]:
因为有两个(的时候dp就置0了,所以需要加上两个dp的计数
- 注意!dp[i]的判断要在表达式内完成!并不是说>1之类的成立了就直接加2了,可能这一部分dp为0,但是还是要+2
题解二 栈
class Solution {
public int longestValidParentheses(String s) {
int ans=0;
Deque<Integer> stack=new LinkedList<>();
stack.push(-1);
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='('){
stack.push(i);
}else{
stack.pop();
if(stack.isEmpty()){
stack.push(i);
}else{
ans=Math.max(i-stack.peek(),ans);
}
}
}
return ans;
}
}
思路:
-
注意这个思路
-
保持栈底的元素为最后一个没有匹配上的右括号的下标。(为了保证如果第一个括号是左括号,栈底不是没有匹配上的右括号的下标,首先在栈底放入-1作为边界条件)将每个(的下标放入栈中,作为匹配,先弹出栈顶元素表示匹配了)。如果遇到),查看栈里有没有元素。如果栈为空,则将)的下标放入栈中,更新最后一个没有匹配上的右括号的下标。如果栈不为空,则将当前)的下标减去栈顶元素的下标,即是目前最长有效括号的长度。
-
注意栈的初始化方法!
Deque stack = new LinkedList();
解法三 最大最小值 贪心法
class Solution {
public int longestValidParentheses(String s) {
int left=0;
int right=0;
int maxNum=0;
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='('){
left+=1;
}else{
right+=1;
}if(left==right){//以right为基准计算括号对的数量,maxNum记录的是全局最大的
maxNum=Math.max(right*2,maxNum);
}else if(right>left){
left=0;
right=0;
}
}
left=0;
right=0;
for(int i=s.length()-1;i>=0;i--){
if(s.charAt(i)=='('){
left+=1;
}else{
right+=1;
}if(left==right){//以left为基准计算括号对的数量,maxNum记录的是全局最大的
maxNum=Math.max(left*2,maxNum);
}else if(right<left){
left=0;
right=0;
}
}
return maxNum;
}
}
思路:
-
利用两个计数器left和right。首先从左到右遍历字符串,遇到(就left+1,)就right+1.当left和right 的值相等时,记录有效字符串的长度,并与最长的长度比较。若right>left时,则将left和right置0,重新计算。
但是这样计算就没有考虑到left>right的情况,即(()这种。所以再从右向左重复差不多的操作,当left>right的时候,left和right都置0。当left=right时,更新有效字符串的长度。