图解LeetCode——761. 特殊的二进制序列(难度:困难)

一、题目

特殊的二进制序列是具有以下两个性质的二进制序列:

  • 0 的数量与 1 的数量相等。
  • 二进制序列的每一个前缀码中 1 的数量要大于等于 0 的数量。

给定一个特殊的二进制序列 S,以字符串形式表示。定义一个操作 为首先选择 S 的两个连续且非空的特殊的子串,然后将它们交换。(两个子串为连续的当且仅当第一个子串的最后一个字符恰好为第二个子串的第一个字符的前一个字符。)

在任意次数的操作之后,交换后的字符串按照字典序排列的最大的结果是什么?

二、示例

2.1> 示例 1

【输入】 S = "11011000"
【输出】 "11100100"
【解释】将子串 "10" (在S[1]出现) 和 "1100" (在S[3]出现)进行交换。这是在进行若干次操作后按字典序排列最大的结果。

说明

  • S 的长度不超过 50
  • S 保证为一个满足上述定义的特殊的二进制序列

三、解题思路

3.1> 思路1:分治 + 递归

其实本题的难点并不在于解法上,而是在于对问题的理解上面。首先,在问题的描述中,一上来就跑出来一个概念,叫:“特殊的二进制序列”,那么对于这个特殊的二进制序列的解释有两点,第一点很好理解,问题就是在第二点上面——“二进制序列的每一个前缀码中 1 的数量要大于等于 0 的数量”,这是什么意思?其实,最初我也没看明白,看了一些解题思路描述,才明白。大多数人在介绍这道题的时候,建议将1看成是“左括号”——(,将0看成是“右括号”——),那么,当我们在使用括号的时候,肯定都是左括号+有括号的——即:(),那么在我们进行数学计算的时候,还会涉及到嵌套计算,例如:((1 + 2) * 3),那么这种情况下,前缀中左括号就是2个,而右括号是1个,即:“((1 + 2)”。

所以,其实这道题,将1和0变为左右括号会更容易理解些。1与0进行匹配,对应着左括号与右括号匹配。以上面示例1为例,S = "11011000"就可以转换为( () ( () ) ),文字不太好看,我们转化为图例,如下所示:

明白了题意之后,我们就可以去思考解题思路了。其实当给我们一个特殊的二进制字符串的时候,我们首要做的就是要将其进行拆分,那么如何进行拆分呢?我们已经知道了,整个特殊的二进制字符串最细的粒度其实就1和0这两个字符,因为满足左括号+右括号。所以,我们可以创建一个变量nums,然后利用String的charAt(...)方法从字符串的头部开始向尾部遍历,当发现是“1”的时候,执行nums++;当发现是“0”的时候,执行nums--;那么当nums等于0的时候,其实就是一个满足特殊二进制的子串了

通过对特殊的二进制字符串的拆分,同级的子串们,我们可以通过调用Collections.sort(splits)来对同级的子串进行排序,那么同样的,对于可再拆分的二进制字符串来说,可以通过遍历的方式,再次细分,然后再通过调用Collections.sort(splits)来对同级的子串进行排序,

例如S=“110010”,我们通过上面介绍的方式,就可以在第一次循环中,将其拆分为“1100”和“10”;但是,也有一种特殊情况,比如S = "11011000",我们发现,在第一次循环遍历之后,“11011000”它自己就是唯一的子串。不过无论子串是它自己,还是可以拆分出多个子串,我们都会在本次循环中,将其放入到List splits集合中,用于后续的排序操作。

当一个字符串可以再度拆分的时候,记得要先将第一个字符和最后一个字符去除掉,再调用makeLargestSpecial(...)方法,以S = "11011000"为例,第一次遍历判断,得到在子序列是它自己“11011000”,那么此时,将首字符“1”和末尾字符“0”去除掉,剩余字符为“101100”,将其作为参数,调用makeLargestSpecial(...)方法,才能实现对其内部进行拆分操作。当然,计算完毕后,最终公钥返回结果的时候,还要将首字符“1”和末尾字符“0”再拼装上。具体实现,请参照——实现1:分治 + 递归

四、代码实现

4.1> 实现1:分治 + 递归

class Solution {
    public String makeLargestSpecial(String s) {
        if (s.length() <= 2) return s;

        List<String> splits = new ArrayList();
        int nums = 0;
        int startIndex = 0;
        for (int i = 0; i < s.length(); i++) {
            if (String.valueOf(s.charAt(i)).equals("1")) ++nums;
            if (String.valueOf(s.charAt(i)).equals("0")) --nums;
            if (nums == 0) {
                splits.add("1" + makeLargestSpecial(s.substring(startIndex + 1, i)) + "0");
                startIndex = i + 1;
            }
        }
        Collections.sort(splits);

        StringBuilder sb = new StringBuilder();
        for (int i = splits.size() - 1; i >= 0; i--) {
            sb.append(splits.get(i));
        }
        return sb.toString();
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值