一天一大 leet(最长有效括号)难度:困难-Day20200704
时间:2022-07-25
本文章向大家介绍一天一大 leet(最长有效括号)难度:困难-Day20200704,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
示例
- 示例1
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
- 示例2
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
抛砖引玉
暴力解法
超时(217 / 230 个通过测试用例)
- 设置开始与结束的指针来切分字符串
- 双层循环,符合要求的字符会在截取过程中出现
- 单数长度一定不符合要求,不进行是否符合校验
- 长度小于已经出现符合要求的字符,不进行是否符合校验
- 判断字符串是否符合要求
- 如果某个元素是'('下一个元素是')',把这两个字符移出,之后重新循环
- 符合要求的字符串会在校验中移除完,有剩余元素则说明不符合要求
/**
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function(s) {
let len = s.length;
let start = 0;
let end = len;
let _result = 0;
while (start < len) {
while (end > start) {
if ((end - start + 1) % 2 && (end - start) > _result) {
let str = s.slice(start, end)
if (!match_str(str)) {
_result = Math.max(_result, end - start);
}
}
end--
}
start++
end = len;
}
return _result
function match_str(str) {
let i = 0;
while (i < str.length - 1) {
if (str[i] === '(' && str[i + 1] === ')') {
str = str.replace('()', '');
i = 0
} else {
i++
}
}
return str.length
}
};
官方答案
动态规划
- 记录遍历字符串记录以每一个元素结尾符合要求的字符长度
- 其中任何一个有效的字符都不会以'('结尾,默认计数是0,记录时遇到'('则不作处理
- 跳过了'(',再累加,得到的累计数都应该是偶数
( |
) |
( |
( |
) |
) |
... |
X |
X |
---|---|---|---|---|---|---|---|---|
dp[0] |
dp[1] |
dp[2] |
dp[3] |
dp[4] |
dp[5] |
... |
dp[i-1] |
dp[i] |
0 |
2 |
0 |
0 |
2 |
dp[5] |
... |
dp[i-1] |
dp[i] |
求dp[5]:
- 如果dp[5]是'('则为0
- 如果dp[5]是')':
- 前面有'('与其匹配
- 前面无'('与其匹配 判断有无匹配的逻辑:
- 上一个组队完成的字符长度即:dp[4]
- 当前指针的位置:5
匹配
匹配位置前一组匹配字符的长度与这次匹配的长度的和:
即:
如果5变成i的话则:如果
,则:
不匹配
那指针在该位置时得到的字符串是不满足条件的
- 上面的例子中dp[4],即dp[i-1]是')',那如果i为2,dp[0]是'('时:
- dp[0]前面没有已经组队完成的字符长度,但是他可以与dp[1]组队 则,s[i-1]为'('时当前组队的长度应该是:
取字符串指定位置字符可以使用'[]'也可以使用chartAt
边界处理:
- i为1时,dp[i - 2]不存在,默认为0
- i-dp[i-1]-2小于0时,dp[i-dp[i-1]-2]不存在,默认为0
/**
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function(s) {
let len = s.length,
_result = 0;
if (len === 0) return _result
let dp = Array(len).fill(0);
for (let i = 1; i < s.length; i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
}
else if (
i - dp[i - 1] > 0 &&
s.charAt(i - dp[i - 1] - 1) == '('
) {
dp[i] = dp[i - 1] +
((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0)
+ 2;
}
_result = Math.max(_result, dp[i]);
}
}
return _result
}
栈
- 像暴力方法的思路,借助索引计算符合规则的字符串长度
- 不同的是,不需要循环截取字符
- 从前到后,如果规则被打断,就从被打断位置从新开始接
- 最终返回最长的字符长度
规则被打断
- '('后面可以逐个为'(',只有')'数量小于'(',才算被打断
- 新建一个数组(栈)来存贮那些可能存在匹配字符的元素索引(用于计算长度)
循环
- 遇到'(',其是起点的标记,存入
- 遇到')',找最近的那个'('与其匹配(从待匹配数组中去掉最后一个元素索引)
匹配字符从索引0开始,那么匹配0时他的起点为-1则,待匹配数组中默认存放-1
- 如果待匹配数组中没有元素了,说明之前的字符已经匹配完了,如果还有匹配,那这个指针所在的位置就是起点
- 如果待匹配数组中还有没有匹配的元素,说明有个字符还没找到匹配项,此时,只需要关注从上个起点到这个指针位置匹配的字符串长度又增加了一个:待匹配数组最后一个是起点,指针是终点
/**
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function(s) {
let maxans = 0;
let stack = [-1];
for (let i = 0; i < s.length; i++) {
if (s.charAt(i) == '(') {
stack.push(i);
continue;
}
stack.pop();
if (!stack.length) {
stack.push(i);
} else {
maxans = Math.max(maxans, i - stack[stack.length - 1]);
}
}
return maxans;
};
正向逆向结合
- '('与')'分别做起点
先从左向右找其中:
- '('数量大于')'则继续查找,之后可能多出的'('会被补全
- ')'数量小于'('则本轮计数停止,统计归零,匹配被打断
- '('数量等于')'则找到字符满足要求,
- '('的计数再循环结束时可能大于')',即:left>right,记录长度是使用2*right,一组'()'
再从右向左找其中:
- '('数量小于')'则继续查找,之后可能多出的')'会被补全
- ')'数量大于'('则本轮计数停止,统计归零,匹配被打断
- '('数量等于')'则找到字符满足要求,记录长度
- ')'的计数再循环结束时可能大于'(',即:right>left,记录长度是使用2*left,一组'()'
返回记录的最大值
/**
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function(s) {
let left = 0,
right = 0,
maxlength = 0;
for (let i = 0; i < s.length; i++) {
if (s.charAt(i) == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * right);
} else if (right > left) {
left = right = 0;
}
}
left = right = 0;
for (let i = s.length - 1; i >= 0; i--) {
if (s.charAt(i) == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * left);
} else if (left > right) {
left = right = 0;
}
}
return maxlength;
};
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 剑指Offer LeetCode 面试题18. 删除链表的节点
- 剑指Offer LeetCode 面试题06. 从尾到头打印链表
- 最详细的docker中安装并配置redis
- 剑指Offer LeetCode 面试题59 - I. 滑动窗口的最大
- 剑指Offer LeetCode 面试题58 - II. 左旋转字符串
- 剑指Offer LeetCode 面试题58 - I. 翻转单词顺序
- 剑指Offer LeetCode 面试题56 - II. 数组中数字出现的次数 II
- 站在软件工程的角度重新思考面向对象(含高清图谱)
- 剑指Offer LeetCode 面试题57. 和为s的两个数字
- 白嫖Layui树型可折叠,可自定义,可搜索表格的实例
- 剑指Offer LeetCode 面试题53 - II. 0~n-1中缺失的数字
- 聊聊dubbo-go的DubboPackage
- 剑指Offer LeetCode 面试题53 - I. 在排序数组中查找数字 I
- Java虚拟机运行时数据区精华总结
- 剑指Offer LeetCode 面试题50. 第一个只出现一次的字符