zoukankan      html  css  js  c++  java
  • [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


     
  • 相关阅读:
    CodeBlocks背景主题的设置
    Win7更改默认打开方式失败
    BAT命令介绍【转自Internet】
    Index of my articles
    一个表格说明RelativeLayout中的几个重要属性【Written By KillerLegend】
    有史以来最简单的三层实例(C#) ——转载自CSDN
    银联规定的MAC算法_转载
    Windows下ORACLE 10g安装与操作图解
    Oracle 在64位机器上使用PLSQL连接Oracle的问题(SQL * NET not properly installed)转载
    Linux系统下JDK和Tomcat安装配置
  • 原文地址:https://www.cnblogs.com/wangxf2019/p/13891705.html
Copyright © 2011-2022 走看看