[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

 



 
posted @ 2020-10-28 16:12  谁在写西加加  阅读(63)  评论(0编辑  收藏  举报