1542 找出最长的超赞子字符串(状态压缩、前缀和 + 哈希思想)

1. 问题描述:

示例 1:
输入:s = "3242415"
输出:5
解释:"24241" 是最长的超赞子字符串,交换其中的字符后,可以得到回文 "24142"

示例 2:
输入:s = "12345678"
输出:1

示例 3:
输入:s = "213123"
输出:6
解释:"213123" 是最长的超赞子字符串,交换其中的字符后,可以得到回文 "231132"

示例 4:
输入:s = "00"
输出:2
提示:
1 <= s.length <= 10^5
s 仅由数字组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-longest-awesome-substring

2. 思路分析:

① 一开始的时候从题目中能够得到的信息是超赞字符串中字符出现的次数那么都是偶数那么只有一个字符出现的次数为奇数这样经过重新排列之后得到的才是回文串。但是后面的话没有想到具体的实现思路,于是看了一下力扣的题解,发现可以使用状态压缩,利用前缀和 + 哈希的思想来解决。其实我们并不太关于字符具体出现的偶数次数与奇数次数是多少我们只需要判断出当前这个字符出现偶数次还是奇数次即可,考虑到只有0-9这个数字,所以我们可以使用1来表示当前的数字出现的次数为奇数,0表示当前出现的字符为偶数,我们在遍历字符的时候就可以通过异或运算计算字符串中每个字符出现的次数是奇数还是偶数(当出现偶数次的时候通过异或运算会将字符出现的次数变为0,奇数的时候会变为1),然后我们就可以使用前缀和 + 哈希表的思想来求解了

② 前缀和 + 哈希的思想:因为使用的是python语言,所以可以使用字典dict来表示哈希表,字典的键保存的是到当前位置字符出现的次数,值表示这个这个序列出现的位置,一开始的时候初始化字典的第一个序列对应的值为-1,这个与之前的前缀和 + 哈希的思想是一致的,并且设置一个int类型的变量sequence用来保存字符出现次数的序列,这样就可以知道到当前位置哪些数字是出现奇数次的哪些是偶数次的(使用异或运算cur ^= 1 << num即可),然后遍历长度范围为0-9的序列,这样通过1 << j(j表示当前遍历的位置)可以表示当前将二进制数字对应的第几位设置为1,使用当前的次数序列进行异或操作:sequence ^ (1 << j),异或的目的是为了将当前某个数字出现奇数的字符保留下来(相当于将这个字符变成了出现偶数次数的字符),因为如果序列的某个数字出现的次数刚好是奇数那么通过异或运算就会将序列中的那个1消掉,我们可以根据这个消掉的结果判断字典中是否存在过这个值,如果存在过那么我就可以将当前出现的奇数次的字符保存下来,这样当前的位置i减去字典中保存的那个序列的位置那么得到的就是那个序列开始位置到当前位置的回文串的长度(相当于从遍历的位置开始减去出现奇数次数的序列的那个位置),这个与之前的前缀和 + 哈希的思路是类似的,都是以当前遍历的字符为右边界,往前找一下是否存在符合条件的答案,而这道题目是以当前遍历到的字符位置为边界,尝试保留某个奇数次的字符的时候看一下之前字典中是否存在过这样的序列,如果存在说明我可以减去之前出现奇数次数的序列开始的位置从而得到以右边界往前的回文串的长度

相当于是求解[j:i]这一段的回文长度

比如字符串"3242415",到遍历到3242的时候记录的次数序列为11000(二进制序列),之前字典中保存的序列为0:-1,1000:0,1100:1,11100:2
使用遍历0-9长度序列,尝试保留出现奇数次数的字符,比如保留4这个字符那么1 << i(i= 4)然后11000 ^ (1 << 4) = 1000而字典中恰好保存了这个值所以把到从开始位置到3这个位置的序列都舍弃掉那么得到就是242这个长度为3的回文串,假如把3这个出现奇数次数的字符保存下来,那么11000 ^ (1 << 3) = 10000,表示到3这个位置前应该出现需要出现10000这个序列但是字典中没有所以无法构成回文串(也很好理解因为4比3出现得要晚而且4又是奇数次数的字符所以在字典中根本就不可能保存这样的序列,而且保留下3说明得到的序列为10000说明到3之前就需要出现这样的序列,这样才可以保证将出现奇数次数的字符4给干掉而4出现在3的后面说明是不可能出现这样的序列也就不能够构成回文),遍历完0-9的长度序列之后那么就需要检查当前的次数序列在字典中是否存在,如果不存在那么将序列保存在字典中,否则就需要计算最大长度了,因为这个时候字典出现过这个序列有可能是全部字符出现的次数都是偶数次数的所以计算一下最大长度

3. 代码如下:

import collections


class Solution:
    def longestAwesome(self, s: str) -> int:
        dic = collections.defaultdict(int)
        sequence = 0
        # 初始化哈希表的第一个位置为-1, 这个与之前的前缀和 + 哈希表的方法是类似的
        dic[0] = -1
        res = 1
        for i in range(len(s)):
            ch = ord(s[i]) - ord("0")
            sequence ^= (1 << ch)
            for j in range(10):    
                # 保留下当前当前出现奇数次的字符
                next = sequence ^ (1 << j)
                if next in dic:
                    res = max(res, i - dic[next])
            if sequence not in dic:
                dic[sequence] = i
            else:
                # 有可能所有字符出现的次数都为偶数
                res = max(res, i - dic[sequence])
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值