[LeetCode] 32. Longest Valid Parentheses 最長有效括號
阿新 • • 發佈:2020-10-28
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 //[email protected] 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 //[email protected] 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