1542. 找出最长的超赞子字符串

1. 题目

1542. 找出最长的超赞子字符串

2. 解题思路

首先来理解一下题目,“超赞字符串”:一个字符串可以重新排序得到一个回文字符串。
那么想达到这个结果,必须有一个前置条件那就是:对每个字符的出现次数进行计数,若出现奇数次的字符的数量不超过 1,则该字符串可以重新排列成回文。
举例说明:
image.png
因为只关心一个字符出现的次数是奇是偶,因此不需要真正的计数,可用1表示该数字出现了奇数次,0 表示该数字出现了偶数次,然后数字范围为0-9一共10个数字。所以使用一个长度为 10 的二进制数(即一个整数的二进制表示)来记录 0-9 每个数字的出现奇偶性。
(即status 是一个 10 位的二进制数,每一位(从低位到高位)分别对应数字 09。如果某一位是 1,表示该位对应的数字出现了奇数次;如果某一位是 0,表示该位对应的数字出现了偶数次。)

1、我们用 status= status^(1<<i) 来达到记录这个数字i的奇偶性的情况。

[!NOTE] status= status^(1<<i)是什么意思呢?
我们直接来举个例子看看,假设有个字符串"24214",那我们初始化status:0000000000 十位二进制数,都为0.
我们处理第一个数字2 那么status = status^(1<<2),拆解一下1<<2 将1向左移2位,得到0000000100,然后将其与原来的status0000000000进行异或(相同为0,不同为1),得到0000000100,这就代表现在的status下,2这个数字出现了奇数次,其余的数字出现了偶数次

2、用一个map来维护每个 status 出现的最早的位置

[!NOTE] 对于2的扩展
综合上面两种操作其实可以得到两个结论 :
1、 如果 status2 和 status1 相同,这意味着从 map.get(status1) 到 map.get(status2) 之间的字符串中,所有数字的奇偶性都没有发生变化。换句话说,在这个区间内,每个数字的出现次数要么都增加了偶数次,要么都减少了偶数次。因此,在这个区间 (map.get(status1), map.get(status2)] 中(注意区间左开右闭),所有数字的出现次数都是偶数次。
例如:
假设我们有字符串 s = "3242415",我们用一个长度为 10 的二进制数来表示状态,每一位对应数字 0-9 的奇偶性。 初始状态status = 0000000000
image.png
2、如果 status2 和 status1 只差一位不同,那么区间 (map.get(status1),map.get(status2)] 中(注意区间左开右闭)有一个数字出现了奇数次,其余数字都出现了偶数次,满足超赞的条件。

然后在满足条件的情况下计算超赞字串的长度,以上面的例子为例,长度即为当前status的位置-第一次出现相同status的位置,即4-0=4 超赞子字符串(为了方便理解,将其顺序重新排列为回文串)的"2442"的长度为4。
根据上述逻辑去遍历处理字符串即可。

3. 代码

3.1. 注意点

[!NOTE] 1、为啥在for循环中不光有个if (!map.containsKey(cur))还要再有个内部循环for (int i = 0; i < 10; i++)
根据我们的解题思路我们可以得知,有两种情况都会出现超赞子字符串,
1-所有数字出现次数都为偶数,这个情况在外层for循环中用if (!map.containsKey(cur))判断就能达到目的。
2-只有一个数字出现奇数次,其余数字出现偶数次,这个情况就需要在内部再来一次for循环去判断了,内部循环的目的是检查通过切换单个数字的奇偶性是否可以找到一个更长的超赞子字符串。
在遍历当前字符的时候需要同时考虑这两种情况,才能取到正确的答案!

[!NOTE] 2、检查是否只有一个数字出现奇数次,其余数字出现偶数次是怎么做的
内部循环遍历数字 0 到 9,通过 cur ^ (1 << i) 来检查如果我们假设数字 i 的奇偶性发生改变(从偶数变奇数或从奇数变偶数),那么当前的状态 cur 变成了 next。通过这种方式,我们可以检查在当前状态下,有没有可能使得其他数字的出现次数都是偶数,只有一个数字的出现次数是奇数。

3.2. 完整代码

class Solution {
    public int longestAwesome(String s) {
        HashMap < Integer, Integer > map = new HashMap < > ();
        int cur = 0; // 当前状态
        int ans = 1; // 记录最长超赞子字符串的长度
        map.put(cur, -1); // 初始状态,所有数字出现偶数次

        for (int c = 0; c < s.length(); c++) {
            int ch = s.charAt(c) - '0'; // 当前字符转换为数字
            // 更新状态
            cur = cur ^ (1 << ch); // 切换对应数字的奇偶性

            // 检查是否只有一个数字出现奇数次,其余数字出现偶数次
            for (int i = 0; i < 10; i++) {
                int next = cur ^ (1 << i); // 改变第 i 位的奇偶性
                if (map.containsKey(next)) {
                    // 更新最长超赞子字符串的长度
                    ans = Math.max(ans, c - map.get(next));
                }
            }

            // 检查当前状态是否存在于 map 中
            if (!map.containsKey(cur)) {
                map.put(cur, c); // 记录当前状态的首次出现位置
            } else {
                ans = Math.max(ans, c - map.get(cur)); // 更新长度
            }
        }
        return ans; // 返回结果
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值