【LeetCode】P32 最长有效括号(未完结)

P32 最长有效括号

题目链接:32. 最长有效括号.

题目描述

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例:

输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”

输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”

题解

方法一:动态规划

思路

  • ①将原问题分解为子问题

子问题:“求以下标为 i i i 的字符结尾的最长有效括号子串的长度”。定义 d p [ i ] dp[i] dp[i] 为以下标为 i i i 的字符结尾的最长有效括号子串的长度,共有 n n n 个子问题(将给定一个只包含 ‘(’ 和 ‘)’ 的字符串记为 s s s n n n 为字符串 s s s 的长度),将这 n n n 个子问题都解决了,那么其中的最大值就是原问题的解了,这就满足了问题具有最优子结构性质

  • ②确定状态

子问题只与一个变量有关:最长有效括号子串结尾字符的下标 i i i i i i 就是状态,所以我们只用一个一维数组就可以存储各个状态的值。共有 n n n 个状态,它们构成状态空间。

  • ③确定一些边界状态(初始状态)的值

不论 s [ 0 ] s[0] s[0] 是什么,因为只有一个括号,所以不可能组成有效括号, d p [ 0 ] = 0 dp[0]=0 dp[0]=0

s [ 0 ] = ‘ ( ’ s[0]=‘(’ s[0]=( s [ 1 ] = ‘ ) ’ s[1]=‘)’ s[1]=),那么这两个字符可以组成有效括号,以下标为 1 1 1 的字符结尾的最长有效括号子串的长度为 2 2 2 d p [ 1 ] = 2 dp[1]=2 dp[1]=2;否则 d p [ 1 ] = 0 dp[1]=0 dp[1]=0

  • ④确定状态转移方程
    当我们在求 d p [ i ] ( i > 1 ) dp[i](i>1) dp[i]i>1

    • s [ i ] = ‘ ( ’ s[i]=‘(’ s[i]=(,以字符 ‘ ( ’ ‘(’ ( 结尾的子串,不可能是一个有效括号子串,所以 d p [ i ] = 0 dp[i]=0 dp[i]=0
    • s [ i ] = ‘ ) ’ s[i]=‘)’ s[i]=),这种情况还要考虑 s [ i − 1 ] s[i-1] s[i1]
      • s [ i − 1 ] = ‘ ( ’ s[i-1]=‘(’ s[i1]=(,现在 s [ i − 1 ] s[i-1] s[i1] s [ i ] s[i] s[i] 已经可以凑成一对有效括号了,那么我们可以考虑: d p [ i ] = dp[i]= dp[i]= 以下标为 i − 2 i-2 i2 的字符结尾的最长有效括号子串的长度 + 2 +2 +2,所以有 d p [ i ] = d p [ i − 2 ] + 2 dp[i]=dp[i-2]+2 dp[i]=dp[i2]+2
      • s [ i − 1 ] = ‘ ) ’ s[i-1]=‘)’ s[i1]=)
        我们知道以 s [ i − 1 ] s[i-1] s[i1] 为结尾字符的最长有效括号子串的长度为 d p [ i − 1 ] dp[i-1] dp[i1] ,我们将这个子串记为 s u b sub sub,字符 s [ i ] = ‘ ) ’ s[i]=‘)’ s[i]=) 要想加入 s u b sub sub,得到一个以字符 s [ i ] s[i] s[i] 为结尾字符的、包含 s u b sub sub 的一个更长的有效括号子串,那么 s u b sub sub 前的一个字符 s [ i − d p [ i − 1 ] − 1 ] s[i-dp[i-1]-1] s[idp[i1]1] 必须为 ‘ ( ’ ‘(’ (,才能和 字符 s [ i ] = ‘ ) ’ s[i]=‘)’ s[i]=) 匹配,若这两个字符匹配,那么还需加上以字符 s [ i − d p [ i − 1 ] − 2 ] s[i-dp[i-1]-2] s[idp[i1]2] 为结尾的最长有效括号子串的长度。即 d p [ i ] = d p [ i − 1 ] + d p [ i − d p [ i − 1 ] − 2 ] + 2 dp[i]=dp[i-1]+dp[i-dp[i-1]-2]+2 dp[i]=dp[i1]+dp[idp[i1]2]+2
        在这里插入图片描述
        由以上的分析,我们可以得到状态转移方程:
        d p [ i ] = { when  s [ i ] = ‘ ( ’       0 when  s [ i ] = ‘ ) ’ { d p [ i − 2 ] + 2 if    s [ i − 1 ] = ‘ ( ’ d p [ i − 1 ] + d p [ i − d p [ i − 1 ] − 2 ] + 2 if    s [ i − 1 ] = ‘ ) ’    &    s [ i − d p [ i − 1 ] − 1 ] = ‘ ( ’ dp[i] = \begin{cases} \text{when } s[i]=‘(’ \ \ \ \ \ 0 \\ \text{when } s[i]=‘)’ \begin{cases} dp[i-2]+2 &\text{if \ \ } s[i-1]=‘(’ \\ dp[i-1]+dp[i-dp[i-1]-2]+2 &\text{if \ \ } s[i-1]=‘)’ \ \ \& \ \ s[i-dp[i-1]-1]=‘(’ \end{cases} \end{cases} dp[i]=when s[i]=(     0when s[i]=){dp[i2]+2dp[i1]+dp[idp[i1]2]+2if   s[i1]=(if   s[i1]=)  &  s[idp[i1]1]=(
  • 原问题的解

最后,在给定的字符串 s s s (长度为 n n n)中,最长有效括号的长度 m a x L e n maxLen maxLen 即数组 d p dp dp 中的最大值。
m a x L e n = max ⁡ ( d p [ i ] ) , ( 0 ⩽ i ⩽ n − 1 , i ∈ Z ) maxLen=\max(dp[i]),(0\leqslant i\leqslant n-1,i\in \mathbb{Z}) maxLen=max(dp[i])(0in1iZ)

算法

class Solution {
public:
	int longestValidParentheses(string s) {	
		if(!s.size()){
			return 0;
		}
		int size=s.size();
		vector<int> dp(size);
		dp[0]=0;
		if(s[0]=='('&&s[1]==')'){
			dp[1]=2;
		}
		for(int i=2;i<size;++i){
			dp[i]=0;
			if(s[i]==')'){
				if(s[i-1]=='('){
					dp[i]=dp[i-2]+2;
				}
				else if((i-dp[i-1]>=1&&s[i-dp[i-1]-1]=='(')){
					dp[i]=i-dp[i-1]>=2?(dp[i-1]+dp[i-dp[i-1]-2]+2):dp[i-1]+2;
				}
			}
		}
		int maxLen=0;
		for(int i=0;i<size;++i){
			maxLen=max(maxLen,dp[i]);
		}
		return maxLen;
	}
};

时间复杂度

假设字符串 s s s 的长度为 n n n

  • 时间复杂度: O ( n ) O(n) O(n),因为动态规划中状态的数目为 n n n,在计算状态 i i i 的值 d p [ i ] dp[i] dp[i] 时,计算时间是一个与 n n n 无关的常数,时间复杂度为 O ( 1 ) O(1) O(1),由动态规划解题的时间复杂度计算公式:
    时 间 复 杂 度 = 状 态 的 数 目 ⋅ 计 算 每 个 状 态 所 需 时 间 时间复杂度=状态的数目⋅计算每个状态所需时间 =所以得到时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n),需要一个长度为 n n n 一维数组 d p dp dp 来存储各个状态的值。

方法二:栈

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值