既然是括号序列,我们可以试着用栈去解决问题,其次,还有动态规划的方法,因为可以发现,这个问题可以拆分成许多子问题。
1.栈
用一个栈来存储括号的下标,思路如下所示:
如果是左括号,压栈。
如果是右括号,弹栈,然后利用当前下标和栈的顶部下标相减就能得到有效括号长度。
但是还有一种情况需要考虑,当栈是空的,没有栈顶怎么办?其实这种情况就是出现了非法的右括号,例如"())()"又或者最开始的情况")()",为了得到其下标,我们需要用一个变量来存储这个位置。
代码如下所示:
class Solution {
public:
/**
*
* @param s string字符串
* @return int整型
*/
int longestValidParentheses(string s) {
// write code here
int res=0;
int start=-1;
stack<int> st;
for(int i=0;i<s.length();++i){
if(s[i]=='('){
st.push(i);
}else{
if(st.empty()){
start=i;
}else{
st.pop();
if(!st.empty()){
res=max(res, i-st.top());
}else{
res=max(res, i-start);
}
}
}
}
return res;
}
};
2.动态规划
分析转移方程的思路如下所示:
设dp[i]是以i下标结尾的最长括号序列。
初始化:
如果是一个左括号,明显dp[i]=0,因为左括号当不了结尾。
如果是一个右括号,那么我们这样做:
- 情况一:
如图所示,左括号隔壁是右括号,那么合法括号需要增加2,可能是这一对括号之前的基础上加,也可能这一对就是起点,因此转移公式为:dp[i]=(i>=2?dp[i−2]:0)+2
- 情况二:
如图所示,与该右括号匹配的左括号不在自己旁边,而是它前一个合法序列之前,因此通过下标减去它前一个的合法序列长度即可得到最前面匹配的左括号,因此转移公式为:dp[i]=(i−dp[i−1]>1?dp[i−dp[i−1]−2]:0)+dp[i−1]+2。
具体实现代码如下所示:
class Solution {
public:
int longestValidParentheses(string s) {
int res = 0;
//长度为0的串,返回0
if(s.length() == 0)
return res;
//dp[i]表示以下标为i的字符为结束点的最长合法括号长度
vector<int> dp(s.length(), 0);
//第一位不管是左括号还是右括号都是0,因此不用管
for(int i = 1; i < s.length(); i++){
//取到左括号记为0,有右括号才合法
if(s[i] == ')'){
//如果该右括号前一位就是左括号
if(s[i - 1] == '(')
//计数+2
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
//找到这一段连续合法括号序列前第一个左括号做匹配
else if(i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(')
dp[i] = (i - dp[i - 1] > 1 ? dp[i - dp[i - 1] - 2] : 0) + dp[i - 1] + 2;
}
//维护最大值
res = max(res, dp[i]);
}
return res;
}
};