位运算、哈希、前缀和、状态压缩

Problem:LeetCode 1371

给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 ‘a’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出现了偶数次。

Method:位运算、哈希、前缀和、状态压缩

今天的这个题真的是太妙了!

First:从最简单开始

首先我们来想暴力破解的算法。无非是枚举所有的子串,然后一一判断。

Second:前缀和

在这个时候,对于每一个子串,我们都要判断aeuio的个数是否为偶数。那么我们就可以维护一个pre数组:pre[i][k] = 从0到i+1的子串中第k个字符的数量。那么对于子串s.substr(i,j-i),其是否满足条件就可以用pre[j][k]-pre[i][k]来判断。再看判断是否为偶数的条件,我们并不需要确切地知道这个数字,只需要知道是否为偶数即可,那么我们就可以用1来代表奇数,0来代表偶数。
不过这个时候我们要判断五次呀,是不是有点麻烦了呢?

Third:二进制以及另类的哈希

对了,我们可以用二进制来进行状态压缩。例如,对于一个从0开始的子串:oghjku,5个字符aeuio的奇偶性用二进制表示即为:00101
那么我们就可以用一个数组存储从0000011111的所有状态即:vector<int> a(1<<5,-1)。其中a[i]表示状态i的二进制第一次出现时当前子串的长度。用-1来赋初值表示未曾有过该状态。

Fourth

对于上述情况,如果在出现过某状态后再次出现相同状态,那么在这之间的子串一定是满足条件的。因为奇数-奇数=偶数;偶数-偶数=偶数。所以当一次状态非第一次出现时,我们记录当前串的长度减去之前第一次出现时的子串长度与当前记录值中的最大值,即:ans = max(ans, i+1-pre[status])

Code:C++

class Solution {
public:
    int findTheLongestSubstring(string s) {
        int status = 0,ans = 0;
        vector<int> pos(1<<5,-1);
        pos[0]=0;
        for (int i = 0; i < (int)s.size(); i++) {
            if (s[i]=='a') status ^= 1 << 0;
            else if (s[i]=='e') status ^= 1<<1;
            else if (s[i]=='i') status ^= 1<<2;
            else if (s[i]=='o') status ^= 1<<3;
            else if (s[i]=='u') status ^= 1<<4;
            if (pos[status]==-1) pos[status] = i+1;
            else ans = max(ans, i+1-pos[status]);
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值