LeetCode --- 字符串系列 --- 分割平衡字符串

分割平衡字符串

题目

在一个「平衡字符串」中,'L' 和 'R' 字符的数量是相同的。

给出一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。

返回可以通过分割得到的平衡字符串的最大数量。


示例

示例 1:

输入:s = "RLRRLLRLRL"
输出:4
解释:s 可以分割为 "RL", "RRLL", "RL", "RL", 每个子字符串中都包含相同数量的 'L' 和 'R'。
示例 2:

输入:s = "RLLLLRRRLR"
输出:3
解释:s 可以分割为 "RL", "LLLRRR", "LR", 每个子字符串中都包含相同数量的 'L' 和 'R'。
示例 3:

输入:s = "LLLLRRRR"
输出:1
解释:s 只能保持原样 "LLLLRRRR".
示例 4:

输入:s = "RLRRRLLRLL"
输出:2
解释:s 可以分割为 "RL", "RRRLLRLL", 每个子字符串中都包含相同数量的 'L' 和 'R'。

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/split-a-string-in-balanced-strings/

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


解题思路

单看上面前三个例子,很容易让人理解错题意

所以我加上了第四个例子

主要在于对字符串的动作是 分割,而不是 切割

处理字符串过程不能舍弃字符串

分割 不舍弃字符串, 切割 会舍弃字符串

本题的题意是 分割 。所以难度是简单

平民思路:
1、
    定义两个对象 A,B。用来存储 R 和 L 当前的字符计数
    A = { R: 0, L: 0 }
    B = { R: 0, L: 0 }
2、
    循环 `平衡字符串` ,判断第一个字符是 R 或者 L,则 A 里面的 R 或 L 属性计数加 1
3、
    计算 A 对象中 R 和 L 的 `计数总和 alen`,用该 `计数总和 alen` 截取 `平衡字符串` ,得到用来做对比的字符串 BChar
    例如 A 中,占据 `平衡字符串` 前两位,则需要第三和第四位与前两位做对比
    即 BChar = string.slice(alen, alen * 2)
4、
    循环 Bchar,判断第一个字符是 R 或者 L,则 B 里面的 R 或 L 属性计数加 1
5、
    此时判断是否满足 `平衡字符串` 条件
    A 里面的 R 计数等于 B 里面的 L 计数: A['R'] === B['L']
    并且 
    A 里面的 L 计数等于 B 里面的 R 计数: A['L'] === B['R']
6、
6、1
    若满足平衡条件,则
    count 加 1
    且 A 重置为 A = { R: 0, L: 0 }
    字符串截取掉对比过的字符串 A + B
    重置 B 为 B = { R: 0, L: 0 }
    循环变量 index 设置为 -1,方便下次循环 index++ 时设置为 0
    继续循环
6、2
    若不满足,则重置 B 为 B = { R: 0, L: 0 }
    继续循环

或者

力扣他人题解

1、
    思考 `平衡字符串` 特性,需要一个 `分割子串` 中的 R 和 L 是相等的
    使用 `出栈入栈` 的方式可以简单明了的解决这个问题
2、
    遍历 `平衡字符串` ,判断第一个字符串是否是 L
    若是,则入栈, stack++,加 1
    若不是,则出栈,stack--,减 1
3、
    这样,如果遍历的前两个字符不一样, stack 加 1,而后又减 1,此时 stack 等于 0
    满足 `平衡字符串` 的条件,那么 count++ 加 1 

题解

此方法是解题常规思路,算是平民解法

所以性能并不是很好,还有点啰嗦

下面给出了力扣题解中比较好的一种解法

 let balancedStringSplit = function(s) {
    let count = 0
    let string = s
    let A = {}, B = {}
    for (let i = 0; i < string.length; i++) {
        // 判断字符是 R 或者 L,则 A 里面的 R 或 L 属性计数加 1
        if (!A[string[i]]) {
            A[string[i]] = 1
        } else {
            A[string[i]]++
        }
        // 计算 A 对象中 R 和 L 的计数总和 alen
        let alen = 0
        Object.keys(A).forEach(key => {
            alen += A[key]
        })
        // 用该总和长度 alen 截取 平衡字符串,得到 用来做对比的字符串 BChar
        // 例如 A 中,占据 平衡字符串前两位,则需要第三和第四位与前两位做对比
        let BChar = string.slice(alen, alen * 2)

        // 循环 Bchar,判断字符是 R 或者 L,则 B 里面的 R 或 L 属性计数加 1
        for (let c of BChar) {
            if (!B[c]) {
                B[c] = 1
            } else {
                B[c]++
            }
        }

        // 判断是否满足平衡字符串条件
        if (A['R'] === B['L'] && A['L'] === B['R']) {
            count++
            i = -1 // 让 i 从 0 开始,这里为 -1,经过循环 + 1 就变成 0 了
            A = {}
            // 字符串截取掉对比过的字符串 A + B
            string = string.slice(alen * 2)
        }
        B = {} // 每次都要重置
    }
    return count
};

或者

力扣他人题解

let balancedStringSplit = function(s) {
    let count = 0
    let stack = 0
    for (let i = 0, len = s.length; i < len; i++ ){
        // 遍历 `平衡字符串` ,判断第一个字符串是否是 L
        // 若是,则入栈, stack++,加 1
        // 若不是,则出栈,stack--,减 1
        if(s[i] === 'L') {
            stack++
        } else {
            stack--
        }
        // stack 等于 0, 满足 `平衡字符串` 的条件,那么 count++ 加 1 
        if (stack === 0) {
            count++ 
        }
    }
    return count
};

备注

如果没有第四个示例,可能造成误解题意。理解为切割字符串,找出平衡子串

并且该平衡子串还是必须是 对称互斥子串。 例如 LLLRRR RL LRLR LLRLRLRR

一开始我理解的就是这样,难得当初写了 N 久,这里也一并贴上算法吧。

但是用的是暴力遍历计算,本来应该要用马拉车算法吧。但是我还没学。先这样吧


思路

1、
创建数组 A
循环 字符串 string
string[i] push 进 A 中

渐进思路 1
1、
    判断 A 的长度如果等于 2
1、1
    继续判断 A 的两个元素是否不一样
        如果不一样,则将这两个元素从 chars 切割出去, T 存储这两个元素
        如果一样,继续循环
2、 
    判断 A 的长度如果大于 2
2、1
    继续判断 A 的长度是否是 2 的倍数
        如果是,对 A 进行对半切割
            然后 [0].join('')  和 [1].reverse().join('') 遍历对比是否全不相等
            如果全不相等,则将这些元素从 chars 切割出去, count++, 外层循环的 index 重置为 内层循环的位置数值,等待下一个外循环 + 1,继续循环
        如果不是,继续循环
最后,返回 T


渐进思路 2
1、
    判断 A 的长度是否是 2 的倍数
1、1
    如果是,对 A 进行对半切割
        然后 [0].join('')  和 [1].reverse().join('') 遍历对比是否全不相等
        若全不相等,则将这些元素从 chars 切割出去, count++, 外层循环的 index 重置为 内层循环的位置数值,等待下一个外循环 + 1,继续循环
    如果不是,继续循环
最后,返回 T


解法

let balancedStringSplit = function(s) {
    let A = [], count = 0
    let string = s
    for (let i = 0; i < string.length; i++) {
        A = []
        A.push(string[i])
        for (let k = i + 1; k < string.length; k++) {
            A.push(string[k])
            let alen = A.length
            // 判断 A 的长度是否是 2 的倍数
            if (A.length % 2 === 0) {
                let judge = true
                let arr = A.slice(0, alen / 2)
                let brr = A.slice(alen / 2).reverse()
                // [0].join('') 和 [1].reverse().join('') 遍历对比是否全不相等
                for (let n = 0; n < arr.length; n++) {
                    if (arr[n] === brr[n]) {
                        judge = false 
                        break;
                    }
                }
                // 若全不相等,则将这些元素从 chars 切割出去, count++, 
                // 外层循环的 i 重置为 内层循环的位置数值
                // 等待下一个外循环 + 1,继续循环
                if (judge) {
                    i = k
                    // string = string.slice(k + 1)
                    // i = -1
                    count++
                    A = []
                    break;
                }
            }
        }
    }
    return count
};
posted @ 2020-04-05 17:21  青S衫%  阅读(758)  评论(0编辑  收藏  举报