1371. 每个元音包含偶数次的最长子字符串
难度:中等
2020/5/20每日一题打卡 √ 前缀和+状态压缩+哈希表
今天2020520,算是一个很特别的日子了!
但是每日一题还是要写的(抄题解的一天)
类似的题:1124. 表现良好的最长时间段
题目描述
解题思路
参考题解:每个元音包含偶数次的最长子字符串 官方题解
❤将题目一步步等价转化 | “非奇即偶” 与二进制的联系
模模糊糊的看懂了一点
首先由 最长连续子串这点想到前缀和,前缀和能效率很高的遍历区间找到符合要求的,比如优美子数组的那道题。然后为了方便的判断两个区间是否满足要求,对状态进行压缩,用5位二进制位来表示每个元音的奇偶性。每出现一个元音字母,就用上一个状态和对应的位进行异或,利用了异或的特性,异或一次是1,两次就是0.
依次求出每个位置的状态,找一找前面有没有相同的状态,如果有就采用最左边那个计算距离,然后找最大的距离。
为什么相同的状态是满足要求的呢,比如一开始的时候是00000,每个元音都没出现过,遍历到某个位置满足要求,每个元音都出现了偶数次,这时状态还是00000,把当前下标和上一个下标相减就得到了区间长度
因为这里状态有限而且能算出来,所以可以用数组来代替哈希表会更高效,用哈希表也可以的
/*
* 1371. 每个元音包含偶数次的最长子字符串
* 2020//5/20每日一题打卡 状态压缩+前缀+哈希表
*/
public int findTheLongestSubstring(String s) {
int n = s.length();
int[] pos = new int[1 << 5]; //位运算,左移 = 1*2^5 用五位二进制位来表示aeiou 存储对应状态出现的最早位置
Arrays.fill(pos, -1); //初始化,没记录就是-1
int re = 0, status = 0;
pos[0] = 0;
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
switch (c) {
case 'a':
status ^= (1<<0); //异或,同0异1
break;
case 'e':
status ^= (1<<1);
break;
case 'i':
status ^= (1<<2);
break;
case 'o':
status ^= (1<<3);
break;
case 'u':
status ^= (1<<4);
break;
default:
break;
}
if(pos[status] >= 0) { //大于等于0说明之前出现过这个状态,维护最大值
re = Math.max(re, i-pos[status]+1);
}else {
pos[status] = i + 1;
}
}
return re;
}