🌈【LeetCode 726. 原子的数量 】- JavaScript(递归/栈+正则)

246 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情


说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)

作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金

GitHub:P-J27、 CSDN:PJ想做前端攻城狮

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


【LeetCode 726. 原子的数量 】- JavaScript(双指针+滑动窗口)

题意描述

给你一个字符串化学式 formula ,返回 每种原子的数量 。

原子总是以一个大写字母开始,接着跟随 0 个或任意个小写字母,表示原子的名字。

如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。

  • 例如,"H2O" 和 "H2O2" 是可行的,但 "H1O2" 这个表达是不可行的。 两个化学式连在一起可以构成新的化学式。

  • 例如 "H2O2He3Mg4" 也是化学式。 由括号括起的化学式并佐以数字(可选择性添加)也是化学式。

  • 例如 "(H2O2)" 和 "(H2O2)3" 是化学式。

返回所有原子的数量,格式为:第一个(按字典序)原子的名字,跟着它的数量(如果数量大于 1),然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。

示例 1:

输入:formula = "H2O"
输出:"H2O"
解释:原子的数量是 {'H': 2, 'O': 1}。

示例 2:

输入:formula = "Mg(OH)2"
输出:"H2MgO2"
解释:原子的数量是 {'H': 2, 'Mg': 1, 'O': 2}。

思路分析:

这个题目可以说是括号匹配升级版的题目,同样需要用栈来解决。这道题实际上是一个比较复杂的字符统计操作。

递归

核心思想:就是嗯模拟,对于括号的情况,括号中的化学式每个原子都要乘以括号外的数值,所以可以取子串(这里用到了括号匹配,由于只需要找与之对应的括号,使用一个变量模拟栈即可),子串也是一个化学式,递归处理即可。

注意点:

  • 同一个原子需要用小写字母这个信息来判断,容易忽视

  • parseNum默认返回1,而不是0

  • 数量1不用append

  • string比较写成-,需要CompareTo

const countOfAtoms = function (formula) {
  const len = formula.length
  const isNumber = (c) => c > 47 && c < 58
  const isUpperChar = (c) => c > 64 && c < 91
  const isLowerChar = (c) => c > 96 && c < 123

  let i = 0
  const counter = () => {
    let element = ''
    let count = 0
    let map = {}

    const counterMap = () => {
      const _count = count || 1
      if (!element) return
      if (typeof element === 'string') map[element] = (map[element] || 0) + _count
      else {
        Object.keys(element).forEach((ele) => {
          map[ele] = (map[ele] || 0) + element[ele] * _count
        })
      }
    }

    while (i < len) {
      const c = formula[i]
      const cCode = c.charCodeAt()
      i++

      if (c === '(') {
        counterMap()
        element = counter()
        count = 0
        continue
      }
      if (c === ')') {
        counterMap()
        return map
      }

      if (isLowerChar(cCode)) element += c
      else if (isUpperChar(cCode)) {
        counterMap()
        element = c
        count = 0
      } else if (isNumber(cCode)) {
        const n = parseInt(c)
        count = count * 10 + n
      }
    }

    if (element || count) counterMap()
    return map
  }

  const map = counter()
  return Object.keys(map)
    .sort()
    .map((ele) => (ele += map[ele] === 1 ? '' : map[ele]))
    .join('')
}

正则

思想:其实不能发现化学表达式都有很明显的字符串特征。首先使用正则表达式解析原子和数字。 正则表达式 /([A-Z][a-z]?)(\d*)/g

注意:正则表达式带有全局修饰符时,循环调用exec方法,方法内部会记录每次执行的索引,结果和match全局的正则表达式类似。合理利用可以方便很多。

var countOfAtoms = function(formula) {
  const map = {}
  let str = formula
  
  while(str.includes('(')) {
    str = str.replace(/\(([A-Z][a-z]*[0-9]*)+\)\d*/g, item => {
      const atomStr = item.match(/[A-Za-z0-9]+/)[0]
      const num = item.match(/\d$/) ? item.match(/\d*$/)[0] : 1
      return atomStr.replace(/[A-Z][a-z]*[0-9]*/g, atom => {
        return atom.match(/\d/) ? atom.replace(/\d+/, inNum => +inNum * +num) : atom + num
      })
    })
  }

  str.match(/[A-Z][a-z]*[0-9]*/g).forEach(item => {
    let key = item.match(/[A-Z][a-z]*/)[0]
    let value = item.match(/\d+$/)
    value = value ? value[0] : 1
    map[key] = map[key] ? (+map[key] + +value) : +value
  })
  return Object.entries(map).map(([key, value]) => {
    return value === 1 ? key : key+value
  }).sort().join('')
   
};

感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。

写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤