文章目录
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
。
那么我们就可以用一个数组存储从00000
到11111
的所有状态即: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;
}
};