美文网首页
# LeetCode 856. Score of Parenth

# LeetCode 856. Score of Parenth

作者: 微微笑的蜗牛 | 来源:发表于2020-03-07 17:26 被阅读0次

@(LeetCode)

问题描述

给定一个括号对匹配的字符串,计算总分数,其中规则如下:

  • () 表示 1
  • AB 表示 A+B 分,其中 AB 都是括号匹配的字符串
  • (A) 表示 2 * A 分,其中 A 是括号匹配的字符串

栗 1:

输入:"()"
输出:1

栗 2:

输入:"(())"
输出:2

解释:
属于 (A) 模式,2 * 1 = 2

栗 3:

输入:"()()"
输出:2

解释:
属于 A+B 模式,1 + 1 = 2

栗 4:

输入:"(()(()))"
输出:6

解释:
一层层计算:2 * (1 + 2 * 1)= 6

注意:

  • S 是括号匹配的字符串,只包括 ()
  • 2 <= S.length <= 50

想看英文原文的戳这里

解题思路

递归解法

题意很明确,三种模式的字符串任意组合,求总分数。

  • AB 模式为 A + B,那只需要分别求出 AB 即可。
  • (A) 模式为 2 * A,需要计算出 A
  • () 为最小原子串,分数为 1

由于只知道 () 的值,因此最终目标是将整个字符串拆分至最小原子串。

字符串的组成规律比较明显,AB 可以看成 AB 两个分组,(A) 拆分出 A 即可。因此可根据括号匹配情况分组,组内再一层层进行拆分,最后拆分至 () 。在拆分过程中识别出 * 2,还是相加求和。

举个栗子,假设 S = (())()(()(()))
其分解过程如下图所示:

QQ20200307-160007@2x.png
  1. S 中有三个分组,(())()(()(()))。一个分组的定义就是最外层() 匹配的字符串,不包括内层。

    那么如何找到 S 中的所有匹配分组?
    从第一个 ( 开始遍历,找到其对应的 ),此时得到一个分组。若 ) 不是最后一个字符,则从 ) 的下一个位置继续找下一分组。

  2. 依次处理分组求和。

  • 若分组为最小单元 (),直接返回 1
  • 若分组不为最小单元,则继续拆分。比如 (()(())),则拆分为 2 * ()(()),然后 ()(())继续按照 1 的步骤重复处理。

拆分过程中最重要的一个点就是:

  • 当匹配字符串为 () 时,直接返回 1。
  • 其他情况,都是 (A) 模式,按 2 * A 的方式处理。

以上处理过程,其实就是递归处理的方式。不断的进行拆分,直至为最小单元。

思路总结如下:

  1. 按括号匹配进行分组,得到 n 个分组,A + B + C + ...
  2. 分别处理 ABC...
  3. 若分组其为 (),则返回 1;否则进行 2 * A 处理,对 A 的处理又从第一步开始。

递归代码如下:

var scoreOfParentheses = function(S) {
    return dfs(0, S.length - 1, S)
};

var dfs = function(l, r, S) {
    if (l < r && r < S.length) {

        // 从 l 开始遍历,寻找最右匹配 )
        let start = l
        let i = l
        let list = []
        
        let sum = 0
        // l-r 之间可能有多组,用 sum 来保存每组值之和
        while (i <= r) {
            // 计算匹配括号
            if (S[i] === '(') {
                list.push(S[i])
            } else if (S[i] === ')') {

                list.pop()

                // 找到匹配的 )
                if (list.length === 0) {
                    // () 模式,直接加 1
                    if (i - start === 1) {
                        sum += 1
                    } else {
                        // (A) 模式,取到 A,继续处理。去除括号,左边+1,右边-1
                        sum += 2 * dfs(start + 1, i - 1, S)
                    }
                    
                    // 下一分组开始
                    start = i + 1
                }
            }

            i += 1
        }

        return sum
    }
    
    return 0
}

栈的解法

使用栈来保存当前已匹配字符串的总分数,这种解法也比较巧妙。

  • 当遇到 (,进栈 0
  • 当遇到 ),表示匹配,计算到当前为止的总分数,即前面的总分数+当下的分数。因为以 ) 结束时,有可能是 2 倍的分数,而当下的分数至少为 1,所以取两者的最大值: score = pre + max(2 * cur, 1)

举个简单的栗子。假设 S = '()',栈初始值为 [0]

  1. 遇到 (0 进栈,栈为 [0, 0]
  2. 遇到 ),计算分数。出栈 2 个元素,分别为 pre = 0cur = 0,则 score = 0 + max(2 * 0, 1) = 1。将其进栈,栈为 [1]
  3. 遍历结束,最终栈中的元素为总分数。

再来个复杂点的。假设S = '()(())',栈初始值为 [0]

  1. 遇到 (0 进栈,栈为 [0, 0]
  2. 遇到 ),计算分数。出栈 2 个元素,分别为 pre = 0cur = 0,则 score = 0 + max(2 * 0, 1) = 1。将总分数进栈,栈为 [1]
  3. 遇到 (0 进栈,栈为 [1, 0]
  4. 遇到 (0 进栈,栈为 [1, 0, 0]
  5. 遇到 ),计算分数。出栈 2 个元素,分别为 pre = 0cur = 0,则 score = 0 + max(2 * 0, 1) = 1。将总分数进栈,栈为 [1, 1]
  6. 遇到 ),计算分数。出栈 2 个元素,分别为 pre = 1cur = 1,则 score = 1 + max(2 * 1, 1) = 3。将总分数进栈,栈为 [3]
  7. 遍历结束,最终栈中的元素为总分数 3

代码如下:

// 使用栈的方式
var scoreOfParentheses2 = function(S) {
    let stack = new Array()
    stack.push(0)

    let i = 0
    while (i < S.length) {
        if (S[i] === '(') {
            stack.push(0)
        } else {
            // 计算分数
            const cur = stack.pop()
            const pre = stack.pop()

            const score = pre + Math.max(2 * cur, 1)
            stack.push(score)
        }

        i += 1
    }

    return stack[0]
};

根据深度求解

这种解法也比较有新意,它将字符串表示为树型结构。

如果是 AB 模式,则表示 AB 是兄弟节点;如果是(A) 模式,表示父子节点。字符串的分解图跟第一种解法的类似。

比如 S = '(()(()()))',其可被分解为:

(10)              第 0 层
/ \
(1) (4)           第 1 层
    /  \
   (1)  (1)       第 2 层

总分数 = 2^1 + 2^2 + 2^2 = 10

主要思路就是求出各个最小原子单元 () 所在的层数 l,然后整体求和 2^l 即可。

代码如下:

// 使用树形结构计算
var scoreOfParentheses3 = function(S) {
    let i = 0
    let sum = 0
    let layer = 0
    while (i < S.length) {
        // 计算层数
        if (S[i] === '(') {
            layer += 1
        } else {
            layer -= 1
        }

        // 最小原子字符串 (),根据层数计算分数
        if (S[i] === ')' && S[i - 1] === '(') {
            sum += 1 << layer
        }
        i += 1
    }

    return sum
}

相关文章

网友评论

      本文标题:# LeetCode 856. Score of Parenth

      本文链接:https://www.haomeiwen.com/subject/fpxhdhtx.html