1371.每个元音包含偶数次的最长子字符串
前缀和+状态压缩
代码
/*
* 1. 记录a e i o u构成整体的状态 范围在00000~11111
* 2. 如果两次出现了相同的状态,
* 假设第一次出现在i处,第二次出现的j处,
* 那么 i+1 ~ j 之间的字符串肯定是满足aeiou出现均为偶数次数的。
* 3. 因为只有经历了偶数个aeiou,才能回到之前的状态。
* 4. 为了使得合理的字符串长度最长,需在第一次出现此状态时,就需要记录到下标,然后下一次碰到相同的状态,就 计算最大长度。
*
*14ms
*时间复杂度 O(n)
*/
class Solution {
public int findTheLongestSubstring(String s) {
int n=s.length();
int[] pos=new int[32];
Arrays.fill(pos, Integer.MAX_VALUE);
int ans=0,status=0;
pos[0]=-1;
for(int i=0;i<n;i++){
//更新子串的状态 u o i e a
char ch=s.charAt(i);
if(ch=='a'){
status^=(1);
}else if(ch=='e'){
status^=(1<<1);
}else if(ch=='i'){
status^=(1<<2);
}else if(ch=='o'){
status^=(1<<3);
}else if(ch=='u'){
status^=(1<<4);
}
if(pos[status]==Integer.MAX_VALUE){
pos[status]=i;
}else{
ans=Math.max(ans, i-pos[status]);
}
}
return ans;
}
}
思路
- 将5个元音字母整体出现的奇偶性 视为一个状态,共2^5即32种状态。
- 如果子串
[0,i]
与子串[0,j]
状态相同,那么子串[i+1,j]
的状态一定是00000,理由如下:- 假设第一次出现在
i
处,第二次出现的j
处,那么i+1 ~ j
之间的字符串肯定是满足aeiou
出现均为偶数次数的。因为只有经历了偶数个aeiou,才能回到之前的状态。- 两个数奇偶性相同,相减一定是偶数
- 两个数奇偶性不同,相减一定时奇数
- 假设第一次出现在
[i+1,j]
的长度是j-(i+1) +1=j-i
;即j-i
就是符合要求的长度。- 为了使得合理的字符串长度最长,只需要记录第一次出现该状态的下标即可。
- 需要注意状态0首次出现的位置应该设定为
-1
原因:- 初始化是status已经设置为0,默认已经出现过。
- 若
pos[0]
不设置为-1
,下标都是从0开始的,假设第一个字母为'l'
,与status异或后,status=0符合要求(子串中各元音字符出现次数为0也成立),长度应为1
。按照i-pos[status]
公式计算,则计算结果为i+Integer.MAX_VALUE
,显然有误。所以pos[0]设置为-1
。
官方代码
/*
*14ms
*时间复杂度 O(n)
*/
class Solution {
public int findTheLongestSubstring(String s) {
int n = s.length();
int[] pos = new int[1 << 5];
Arrays.fill(pos, -1);
int ans = 0, status = 0;
pos[0] = 0;
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
if (ch == 'a') {
status ^= (1 << 0);
} else if (ch == 'e') {
status ^= (1 << 1);
} else if (ch == 'i') {
status ^= (1 << 2);
} else if (ch == 'o') {
status ^= (1 << 3);
} else if (ch == 'u') {
status ^= (1 << 4);
}
if (pos[status] >= 0) {
ans = Math.max(ans, i + 1 - pos[status]);
} else {
pos[status] = i + 1;
}
}
return ans;
}
}
- 官方代码中,p[0]=0?计算长度i+1-pos[status] 和 pos[status]=i+1不是特别理解
参考链接: