一天一大 lee(有效的括号)难度:简单-Day20200814

时间:2022-07-25
本文章向大家介绍一天一大 lee(有效的括号)难度:简单-Day20200814,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目:

给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注意空字符串可被认为是有效字符串。

示例

  1. 示例 1
输入: "()"
输出: true
  1. 示例 2
输入: "()[]{}"
输出: true
  1. 示例 3
输入: "(]"
输出: false
  1. 示例 4
输入: "([)]"
输出: false
  1. 示例 5
输入: "{[]}"
输出: true

抛砖引玉

原错误暴力解法!!!!!!!

原本的思路

  • 使用双指针从开始和结束位置去查询匹配
  • 优先匹配最远的一个闭合标签
  • (为了避免真实操作字符串)使用哈希记录已经被匹配的标签所有

实现

  • 声明 dp 存储匹配索引:闭标签->开标签
  • 遍历已匹配的不再使用

特殊情况

  • s 长度不为偶数返回 false
  • 空字符串返回 true
/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
  let len = s.length
  if (s === '') return true
  if (len % 2 !== 0) return false
  let map = new Map([
      ['(', ')'],
      ['[', ']'],
      ['{', '}'],
    ]),
    dp = new Map(),
    start = 0,
    end = len-1
  while (start < end) {
    let a = s.charAt(start),
        b = s.charAt(end);
    if(map.get(a) && map.get(a) === b && !dp.has(end)){
      // 校验两个匹配的字符使用去其他匹配字符存在索引的交错
      // 交错则不满足
      if(!check(start,end,dp)) return false
      dp.set(end,start);
      start++;
      end = len-1;
    }else{
      if(dp.has(start)) {
          start++;
          end = len-1;
      }else{
        end--
      }
    }
  }

  // 检查是否有字符被截断
  function check(start,end,dp){
    for(let i = start;i<end;i++){
      if(dp.get(i) && dp.get(i) > end ||dp.get(i) < start) return false
    }
    return true
  }
  return dp.size === parseInt(len / 2, 10)
};

提交错位:错位示例:"[({(())}[()])]"

上面的逻辑优先匹配最远的字符, 但是存在区间内存在多个如果存在分别于首位形成闭合的字符则结果错误。这样匹配最远的字符思路是有问题的


抛砖引玉

匹配逻辑:从前向后遍历,新增加的元素向上一位匹配,匹配成功则与其组队移除

思路

  • 从开始遍历,每个元素向前找配对字符
  • 优先选择前面最近的闭合字符
  • 如果直接遍历会有两个问题使指针不连续:
    • 存在与遍历过元素匹配的位置不确定
    • 匹配过的字符位置遍历指针需要跳过
  • 指针不连续是常规遍历变的复杂
  • 可以声明dp存放待匹配字符,遍历是在dp中配对,配对成功则移除
  • 配对仅配对最后一个字符(符合栈的逻辑:先进后出)

特殊情况

  • s 长度不为偶数返回 false
  • 空字符串返回 true

  • dp中优先存贮起始字符
  • 逐个遍历,查找闭合字符
  • 新遍历的字符向前匹配最近的配对字符
  • 匹配上则将配对字符清除
  • 不然放置到待匹配字符dp中
/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function (s) {
  let len = s.length
  if (s === '') return true
  if (len % 2 !== 0) return false

  let map = new Map([
    [')', '('],
    [']', '['],
    ['}', '{'],
  ]),
  dp = []
  s.split('').forEach(i => {
    if (map.has(i)) {
      // 判断dp中最后的字符是否能与新遍历字符匹配
      if (!dp.length || dp[dp.length - 1] !== map.get(i)) return false
      dp.pop()
    } else {
      // 如果是开始字符直接存放到dp中
      dp.push(i)
    }
  })
  return !dp.length
}

正则

  • 使用正则替换闭合字符对
  • 全部替换完成则匹配成功否则失败
/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
  if (s === '') return true
  if (s.length % 2 !== 0) return false
  let len = s.length/2;
  for (let i = 0; i < len; i++) {
    s = s.replace(/(())|([])|({})/, '');
  }
  return s.length === 0
}