0
点赞
收藏
分享

微信扫一扫

[动态规划]BM77 最长的括号子串-较难

​​BM77 最长的括号子串​​

知识点​​字符串​​​​动态规划​​​​栈​​

描述

给出一个长度为 n 的,仅包含字符 '(' 和 ')' 的字符串,计算最长的格式正确的括号子串的长度。

例1: 对于字符串 "(()" 来说,最长的格式正确的子串是 "()" ,长度为 2 .

例2:对于字符串 ")()())" , 来说, 最长的格式正确的子串是 "()()" ,长度为 4 .


字符串长度:[动态规划]BM77 最长的括号子串-较难_栈


要求时间复杂度 [动态规划]BM77 最长的括号子串-较难_贪婪_02 ,空间复杂度 [动态规划]BM77 最长的括号子串-较难_贪婪_02.

示例1

输入:

"(()"

复制返回值:

2

复制

示例2

输入:

"(())"

复制返回值:

4

题解

动态规划解法

  1. 使用dp[i]表示以下标i结尾时最长括号子串的长度
  2. 初始条件:dp[0] = 0,表示0个、1个字符时合法的括号子串的长度
  3. 状态转移方程:
  1. 如果dp[i]是),且dp[i-1]是(,那么dp[i] = dp[i-2] + 2
  2. 如果dp[i]是),且dp[i-1]是),那么需要去除掉dp[i-1]的合法长度之后,判断其前一位置是否为(,如果是则合法长度为dp[i] = dp[i - dp[i-1] - 1] + 2,如果不是(那么dp[i] = 0

代码如下:

int longestValidParentheses_dp(std::string &s)
{
int max_length = 0;
std::vector<int> dp(s.size(), 0);
for (int i = 1; i < s.size(); ++i)
{
if (s[i] == '(')// 不对左括号做统计
{
continue;
}

// s[i] == ')'
if (s[i - 1] == '(')// 前一字符为(,刚好和s[i]匹配,在dp[i-2]的长度上+2即可
{
dp[i] = dp[i - 2] + 2;
}
// s[i-1]为右括号,由于dp[i-1]记录了右括号匹配的长度,继续判断去处这个长度之后前面是否有匹配的左括号
// 比如(()()),对于i=5,dp[i-1]=4,i-dp[i-1]-1的值为0,因此判断的是s[0]是否为左括号
else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(')
{
// dp[i] = dp[i - 1] + 2 表示的是s[i-1]这个地方的右括号原来的长度+当前匹配的左右括号长度
// dp[i - dp[i - 1] - 2]则表示和s[i]匹配的左括号的左边一位的值
// 比如()(()()),总长度为8,当i等于7的时候
// i-1=6,dp[i-1]=4,dp[1]=2,dp[i-dp[i-1]-2] = dp[7-4-2] = dp[1] = 2
// dp[i] = dp[i-1] + 2 + dp[i - dp[i - 1] - 2] = dp[6] + 2 + dp[1] = 4 + 2 + 2 = 8
dp[i] = dp[i - 1] + 2 + (i - dp[i - 1] >= 2 ? dp[i - dp[i - 1] - 2] : 0);
}

max_length = std::max(max_length, dp[i]);
}
return max_length;
}

栈解法


贪婪解法

遍历字符串,并使用2个计数器分别统计左括号的个数,以及右括号的个数。我们知道当到达某一索引i的时候,如果括号子串是合法的,此时应该左括号个数大于等于右括号的个数,如果右括号个数大于左括号个数则到达当前位置时的子串是非法的。在左右括号个数相等的时候记录最大长度,当右括号个数大于左括号个数时清零计数器。

在正序遍历完字符串后,还需要逆序遍历一遍,因为对于(()这样的输入,从左到右的遍历会导致左括号数和右括号数永远无法匹配。

代码如下:

int longestValidParentheses(string s)
{
int left_count = 0;
int right_count = 0;
int max_count = 0;
for (int i = 0; i < s.size(); ++i)
{
if (s[i] == '(')
{
left_count++;
}
else
{
right_count++;
if (right_count == left_count)// 这里需要严格相等,因为(()这种是非法的,如果使用right<left,那么会将非法的情况统计进去
{
max_count = std::max(max_count, right_count * 2);
}
else if (right_count > left_count)
{
right_count = left_count = 0;
}
}
}

left_count = right_count = 0;
for (int i = s.size() - 1; i >= 0; --i)
{
if (s[i] == ')')
{
right_count++;
}
else
{
left_count++;
if (left_count == right_count)
{
max_count = std::max(max_count, right_count * 2);
}
else if (left_count > right_count)
{
right_count = left_count = 0;
}
}
}

return max_count;
}

一种看似正确的错误解法

// 错误case:"()(()",预期输出2,实际输出4
// 因为(()这样的输入应该是非法的,但是这种统计方法却认为是合法的
// 需要严格检查左右括号是否完全相等,而不是仅仅进行个数的统计,改变后就又变成了第三种贪婪解法了
int longestValidParentheses_wrong(string s)
{
int max_length = 0;
int left_count = 0;
int count = 0;
for (int i = 0; i < s.size(); ++i)
{
if (s[i] == '(')
{
left_count++;
continue;
}

if (left_count == 0)
{
count = 0;
continue;
}
left_count--;
count++;
if (count > max_length)
{
max_length = count;
}
}
return max_length * 2;
}

举报

相关推荐

0 条评论