利用二进制进行状态压缩
当我们遍历数据结构时,需要对某个状态进行保存时,我们一般用字典或者数组来记录,这样做其实比较浪费空间。如遍历字符串时记录元音 ( a 、 e 、 i 、 o 、 u ) (a、e、i、o、u) (a、e、i、o、u)出现次数的奇偶性,一般用unordered_map<char,int>times,每出现一次元音x,time[x]++,遍历完后判断time[x]的奇偶性。很费空间。
其实我们可以发现,奇偶性实际上只有两种结果,0 o r or or 1,0为偶,1为奇。不妨我们用5位二进制 00000 00000 00000 来表示 ( a 、 e 、 i 、 o 、 u ) (a、e、i、o、u) (a、e、i、o、u)的奇偶性,那么一共2^5=32种状态,由于大部分语言不直接提供二进制数据类型,我们不妨用整型state来代替二进制,那么state的取值范围是0~31。
如果用
S
(
i
,
j
)
S(i,j)
S(i,j)表示
i
到
j
i到j
i到j的状态(元音出现次数的奇偶性,一共32种),若
S
(
0
,
i
)
=
S
(
0
,
j
)
S(0,i)=S(0,j)
S(0,i)=S(0,j),那么
S
(
i
+
1
,
j
)
S(i+1,j)
S(i+1,j)必定为0 。
为什么?因为状态相同说明5个bit个个相同,因为每出现一个元音bit都要改变一次(取一次异或),bit没变就说明bit取了偶数次异或
利用这个原理,我们就可以找到每个元音都出现偶数次的最长子串:
class Solution {
public:
//我们就可以从前往后遍历,记录每种状态第一次出现和最后一次出现的位置,位置差就是该状态的下的最长子串长,遍历所有状态即可得到答案
int findTheLongestSubstring(string s) {
int n=s.size();
int state=0;
vector<vector<int>> indexs(32,vector<int>(2,-2));//记录每种状态第一次和最后一次的位置
indexs[0][0]=-1; //空串时,状态为0
for(int i=0;i<n;i++){
switch(s[i]){
case 'a':state^=16;break;
case 'e':state^=8;break;
case 'i':state^=4;break;
case 'o':state^=2;break;
case 'u':state^=1;break;
}
if(indexs[state][0]==-2)//第一次出现
indexs[state][0]=i;
indexs[state][1]=i;//最后一次出现
}
int start=0,end=-1;
for(int i=0;i<32;i++)
if((end-start)<(indexs[i][1]-indexs[i][0])){
start=indexs[i][0]+1;//S(0,i)=S(0,j),那么S(i+1,j)必定为0 。
end=indexs[i][1];
}
return end-start+1;
}
};
总结:若需要记录若干个变量的状态,且这些变量的状态只有0 o r or or 1,那么可以考虑用二进制数来代替数组和字典,状态发生变化时用1进行异或