[LeetCode] 32. 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 "()()"
分析:
结合题目“求最长有效括号子串长度”,是一个求最值的问题可以考虑尝试使用动态规划进行分析。使用动态规划解题通常分为
定义状态、状态转移方程、处理base case 三步。
第一步:定义状态
定义状态数组dp_a[N],可用dp_a[i] 表示字符串 s[0:i]的最长有效括号子串长度,dp_a[i]根据s[i]的值,有不同的结果,
当 s[i]=='('时, dp_a[i] =dp_a[i-1],
当s[i] == ')'时,dp_a[i] = max(dp_a[i-1],dp_b[i]),其中dp_b[i]表示s[0:i]中,以s[i]结尾的最长有效括号子串长度。
经过以上分析,涉及两个状态,dp_a[i]表示s[0:i]的最长有效括号子串长度,dp_b[i]表示s[0:i]中,以s[i]结尾的最长有效括号子串长度。
dp_a[s.size()]是最终的结果。dp_a[i]的状态转移只和dp_a[i-1]和dp_b[i]有关,所以本题分析dp_b[i]的状态转移情况。
第二步:定义状态转移方程
由第一步状态定义和状态转移的选择分析,分析定义状态方程:
1. 当 s[i] == '(' :
dp_a[i] = dp_a[i-1]
dp_b[i] = 0;
2. 当 s[i] == ')':
dp_a[i] = max(dp_a[i-1],dp_b[i])
2.1 若 s[i-1] == '(': //"****()"
dp_b[i] = dp_b[i-2] +2 //"****()"
2.2 若 s[i-1] == ')' //"****))"
若 dp_b[i-1]等于0,则,dp_b[i] = 0
若dp_b[i-1]大于0,则找到dp_b[i-1]对应子串的第一个字符的前一个字符的位置 pre_index,
若pre_index >=0 ,且s[i] = '(',
则 dp_b[i] = dp_b[i-1] + 2 + front,否则,dp_b[i] = 0;
front表示dp_b[i]对应字符串的首位字符的前一位的dp_b[]状态,
若 pre_index >= 0,fornt = dp_b[pre_index -1],否则 front = 0;
可见本题的难点就在 2.2中 状态转移方程的讨论,因为dp_a[i]= max(dp_b[k],0<=k<=i),
所以可以本题可以只求dp_b[i]表示的状态,
从中取得最大值,即可得到最终的结果
第三步:处理base case
由第二步分析得到的状态转移方程,求状态dp_b[i],需要求dp[i-1]和dp[i-2],
i 从2开始,所以base case 就是 i = 0 和i = 1的情况 。
dp_b[0] = 0;
dp_b[1] = s[0]=='('&&s[1]==')'?2:0;
本题的状态转移方向是从数组s[]的最低位到最高位
代码如下: 时间复杂度 O(n),空间复杂度O(n),
由于求dp[i]需要dp[i-1]和dp[i-2]两个已知状态,所以暂时还未想到将空间消耗压缩到O(1)的方法。
1 /* 2 * @Descripttion: 3 * @version: 4 * @Author: wangxf 5 * @Date: 2020-08-21 19:12:09 6 * @LastEditors: Do not edit 7 * @LastEditTime: 2020-10-26 01:13:54 8 */ 9 /* 10 * @lc app=leetcode.cn id=32 lang=cpp 11 * 12 * [32] 最长有效括号 13 */ 14 //xfwang161631@gmail.com 15 // @lc code=start 16 #include<stack> 17 using namespace std; 18 class Solution { 19 public: 20 int longestValidParentheses(string s) 21 { 22 // dynamic planning 23 if(s.empty()||s.size()<2) return 0; 24 const int n =s.size(); 25 //define status 26 int dp[n]; 27 //base case 28 dp[0] = 0; 29 dp[1]= (s[0]=='('&&s[1]==')'?2:0); 30 int res =dp[1]; 31 //status move 32 for(int i=2;i<s.size();++i) 33 { 34 if(s[i]=='(') 35 { 36 dp[i] = 0; 37 } 38 else 39 { 40 if(s[i-1]=='(')//s[i-1]=='(',s[i] == ')' 41 { 42 dp[i] = dp[i-2]+2; 43 } 44 else //s[i-1]==')',s[i] == ')' 45 { 46 int pre_index = i-1-dp[i-1]; 47 dp[i]= dp[i-1]!=0&&pre_index >= 0 &&s[pre_index]=='('?dp[i-1]+2:0; 48 if(pre_index-1>=0&&dp[i]>0) 49 { 50 dp[i] += dp[pre_index-1]; 51 } 52 } 53 } 54 res = max(res,dp[i]); 55 } 56 return res; 57 } 58 }; 59 // @lc code=end
方法二: 栈 + 哈希
本题同样可以使用 栈结合 哈希的方法 解决。栈存储左括号的数组下标值。哈希的key存储匹配到的左右括号对的右括号数组下标,
value存储对应的左括号的数组下标值。
思路:用dp[i]表示s[0:i]中以s[i]结尾的最长有效括号子串长度。在遍历s[n]的过程中,
遇到‘(’将其对应的数组下标值入栈,dp[i] = 0 。遇到‘)’则查看栈是否为空,栈空则dp[i] = 0。
栈不空,取栈顶 j ,j就是当前‘)’对应匹配的‘(’的下标 ,dp[i] = i - j + 1 + dp[j-1],
在哈希中查询 j-1,查询不到 则 dp[j-1] = 0 ,查询得到,则循环查询,具体参考代码。
代码如下,时间复杂度:O(n^2) 空间复杂度 O(n),相比方法一,时间空间上都比较差,工程上,想不到方法一可以使用。
1 /* 2 * @Descripttion: 3 * @version: 4 * @Author: wangxf 5 * @Date: 2020-08-21 19:12:09 6 * @LastEditors: Do not edit 7 * @LastEditTime: 2020-10-26 01:13:54 8 */ 9 /* 10 * @lc app=leetcode.cn id=32 lang=cpp 11 * 12 * [32] 最长有效括号 13 */ 14 //xfwang161631@gmail.com 15 // @lc code=start 16 #include<stack> 17 using namespace std; 18 class Solution { 19 public: 20 int longestValidParentheses(string s) 21 { 22 if(s.empty()) return 0; 23 const int n =s.size(); 24 //base case 25 int dp_a = 0; 26 stack<int> index_stack;//辅助栈 27 map<int,int> query_map; 28 //状态转移 29 int res = 0; 30 for(int i = 0;i<s.size();++i) 31 { 32 if(s[i]=='(') 33 { 34 index_stack.push(i); 35 dp_a = 0; 36 } 37 else //s[i]==')' 38 { 39 if(index_stack.empty()) 40 { 41 dp_a = 0; 42 } 43 else 44 { 45 int index = index_stack.top(); 46 int host_len = i - index + 1; 47 int prefix_len = 0; 48 while(query_map.find(index-1)!=query_map.end()) 49 { 50 prefix_len+= ((index-1) - query_map[index-1] +1); 51 index = query_map[index-1] ; 52 } 53 dp_a = host_len + prefix_len; 54 query_map[i] = index; 55 index_stack.pop(); 56 } 57 } 58 res = max(res,dp_a); 59 } 60 return res; 61 } 62 }; 63 // @lc code=end