题目描述
- (括号题真的好烦人)
- 讲道理,题目一看,大概率就是用dp做
代码 & 解析
1. 栈做法
- 这个做法我没实际写,但是感觉很厉害,就记录一下。
- 我们之前做括号正确判断的时候,就用到了栈来进行匹配判断。
如今也可以用栈来进行正确判断,并记录下可行的括号的下标,
比如(1,4)(2,3)(7,8),得到142378,而后一次排序O(nlogn),得到123478。
在这个有序数组中找到最长连续子串即可。
(当然,这里可以优化成O(n),排序用其他方法代替。)
2. 动态规划
- 具体见注释
/**
* 使用dp做法
* 我是觉得,难点在于考虑dp数组的设计(一维、二维)
* 以及dp数组存储值代表的意思
* 再就是循环的设计(头到尾、尾到头、短到长)
* 最后是状态转移方程的设计,这个要根据实际问题来,感觉是最难的:
* 1. 有哪几种转移情况?(分类,真的不容易:要找出最合适的分类,并且要分类包括所有情况)
* 2. 为什么转移,为什么可以这么转移
*/
class Solution {
public int longestValidParentheses(String s) {
if (s == null || s.length() == 0){
return 0;
}
int ans = 0;
int len = s.length();
// dp[i]代表以s.charAt[i]结尾的最长有效括号
int[] dp = new int[len];
// O(n)
for (int i=0; i<len; i++){
char ch = s.charAt(i);
// 这种情况肯定是0,"xxxxxxx...(",以这玩意结尾必然无效:可以省略不写,默认为0
/*
if(ch == '('){
dp[i] = 0;
}
*/
// ')'有两种情况
if(i > 0 && ch == ')'){
// 刚好契合的情况,构成"xxxxxx()"
if(s.charAt(i-1) == '(') {
// 加上之前的最长有效括号
dp[i] = i-2 >= 0? dp[i-2] + 2 : 2;
}
// "xxxxxx))"的情况
else{
// 用【】来代表dp[i]组成的括号:"xx【xxxxx)】"
// 此时 s.charAt[i - dp[i-1]-1]可能是数组越界,或者是'('、')'
int left = i - dp[i-1]-1;
if(left >= 0 && s.charAt(left)=='('){
dp[i] = left-1>=0?dp[i-1]+2+dp[left-1] : dp[i-1] + 2;
// "x【"中延续x的长度
}
/* //可以省略,默认0
else{
dp[i] = 0;
}
*/
}
ans = Math.max(dp[i],ans);
}
}
return ans;
}
}
- 整理一下
class Solution {
public int longestValidParentheses(String s) {
if (s == null || s.length() == 0){
return 0;
}
int ans = 0;
int len = s.length();
// dp[i]:以s.charAt[i]结尾的最长有效括号
int[] dp = new int[len];
// O(n)
for (int i=0; i<len; i++){
char ch = s.charAt(i);
// Case 1: "xx(",以'('结尾必然无效,默认为0
// Case 2: ')'有两种情况
if(i > 0 && ch == ')'){
// 刚好契合的情况,构成"xxxxxx()"
if(s.charAt(i - 1) == '(') {
// 加上之前的最长有效括号
dp[i] = i - 2 >= 0 ? dp[i - 2] + 2 : 2;
}
// "xxxxxx))"的情况
else{
// 用【】来代表dp[i]组成的括号:"xx【xxxxx)】"
// 此时 s.charAt[i - dp[i-1]-1]可能是数组越界,或者是'('、')'
int left = i - dp[i - 1] - 1;
if(left >= 0 && s.charAt(left)=='('){
dp[i] = left - 1 >= 0 ?
dp[i - 1] + 2 + dp[left - 1] : dp[i - 1] + 2;
// "x【"中延续x的长度
}
// else 为默认0,省略不写
}
ans = Math.max(dp[i], ans);
}
}
return ans;
}
}