内容介绍
给你一个只包含
'('
和')'
的字符串,找出最长有效(格式正确且连续)括号子串
的长度。示例 1:
输入:s = "(()" 输出:2 解释:最长有效括号子串是 "()"示例 2:
输入:s = ")()())" 输出:4 解释:最长有效括号子串是 "()()"示例 3:
输入:s = "" 输出:0提示:
0 <= s.length <= 3 * 104
s[i]
为'('
或')'
完整代码
int longestValidParentheses(char* s) {
int maxans = 0, n = strlen(s);
if (n == 0) return 0;
int dp[n];
memset(dp, 0, sizeof(dp));
for (int i = 1; i < n; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] +
((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = fmax(maxans, dp[i]);
}
}
return maxans;
}
思路详解
整体思路
- 动态规划:使用一个一维数组
dp
来存储以每个位置i
结尾的最长有效括号子串的长度。 - 初始化:初始化
dp
数组为0,因为单个字符不可能形成有效的括号对。 - 遍历字符串:从左到右遍历字符串
s
,对于每个字符,根据其是(
还是)
来更新dp
数组。 - 更新规则:
- 如果当前字符是
)
,检查其前一个字符是否是(
,如果是,则可以形成一个长度为2的有效括号对。 - 如果前一个字符不是
(
,则检查dp[i-1]
之前的位置,看是否能与当前位置形成有效括号对。
- 如果当前字符是
- 记录最大长度:在遍历过程中,记录下
dp
数组中的最大值,即为最长有效括号子串的长度。
详细步骤
-
初始化变量:
maxans
用于记录最长有效括号子串的长度,n
是字符串s
的长度。 -
边界条件:如果字符串为空,直接返回0。
-
动态规划数组:创建一个大小为
n
的数组dp
,并初始化所有元素为0。 -
遍历字符串:从第二个字符开始遍历字符串(因为第一个字符单独不可能形成有效括号对)。
-
判断当前字符:
- 如果当前字符是
)
,进行以下判断:- 直接匹配:如果前一个字符是
(
,则可以形成一个长度为2的有效括号对,dp[i] = dp[i - 2] + 2
(如果i >= 2
)。 - 间接匹配:如果前一个字符是
)
,则检查dp[i - 1]
之前的位置,如果i - dp[i - 1] - 1
处的字符是(
,则可以与当前位置的)
形成有效括号对,dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2
(如果i - dp[i - 1] >= 2
)。
- 直接匹配:如果前一个字符是
- 如果当前字符是
-
更新最大长度:在每次更新
dp[i]
后,使用fmax
函数更新maxans
为dp[i]
和maxans
中的较大值。 -
返回结果:遍历结束后,
maxans
即为最长有效括号子串的长度。
代码注释说明
dp[i]
:表示以位置i
结尾的最长有效括号子串的长度。if (s[i] == ')')
:检查当前字符是否为)
。if (s[i - 1] == '(')
:检查前一个字符是否为(
。if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(')
:检查dp[i - 1]
之前的位置是否为(
。
知识点精炼
动态规划
- 状态定义:
dp[i]
表示以s[i]
结尾的最长有效括号子串的长度。 - 状态转移:根据当前字符及其前一个字符和
dp
数组中的值来更新dp[i]
。
字符串遍历
- 从左到右:顺序遍历字符串,确保每个位置都可以根据之前的状态进行更新。
特殊情况处理
- 空字符串:直接返回0,因为没有字符可以形成括号。
- 单个字符:单个字符无法形成有效括号,因此
dp
数组初始化为0。
有效括号匹配
- 直接匹配:如果
s[i] == ')'
且s[i-1] == '('
,则可以直接形成长度为2的有效括号对。 - 间接匹配:如果
s[i] == ')'
且s[i-1] == ')'
,则检查s[i - dp[i - 1] - 1]
是否为'('
,如果是,则可以与前面的有效括号对合并。
数组操作
- 初始化:使用
memset
初始化dp
数组,设置所有元素为0。 - 更新最大值:使用
fmax
函数在遍历过程中更新最长有效括号子串的长度。
逻辑判断
- 边界检查:在更新
dp[i]
时,确保索引不会越界。
性能优化
- 空间复杂度:使用一维数组
dp
,空间复杂度为O(n)。 - 时间复杂度:只遍历字符串一次,时间复杂度为O(n)。