在一次采访中,我被要求检查天气,给定的字符串有重复的字符。谷歌关于这个问题,我开始了解一个使用位操作的问题。
bool check(char*name)
{
int i;
int checker=0;
for(i=0;name[i]!=0;i++)
{
int val=name[i]-'a';
if((checker&(1<<val))>0)return false;
checker|=(1<<val);
}
return true;
}
我检查了这段代码并且工作正常。但我不理解这一行背后的逻辑。
> if((checker&(1<<val))>0)return false;
> checker|=(1<<val);
第二个疑问是,如果字符串太长或包含Unicode(2字节宽字符),这是否可行?
答案 0 :(得分:1)
该算法使用每个ascii字符1位来指示该集合的存在。所以它至少适用于英文小写字母 - 其中26个字符和连续的ascii代码。 a = 000001,b = 000010,c = 000100等 &#39; aacaaccc&#39;和&#39; ac&#39;和&#39; ca&#39;无论a和c的出现次数如何,都将编码为000101。因此,字符串长度并不重要。
你对2字节字符是正确的。拉丁字符集也会引起问题,但混合情况(上下)的问题可以通过屏蔽第5位(32)转换为大写(或者使用32转换为小写)来轻松解决。 / p>
ASCII字符表为所有字符分配一个整数:
@ = 64 = 01**0**00000 ...
A = 65 = 01**0**00001 ... a = 97 = 01**1**00001
B = 66 = 01**0**00010 ... b = 98 = 01**1**00010
..
Z = 90 = 01**0**11010 ... z = 122 = 01**1**11010
大写和小写字符仅在该特定位和'a' - 32 == 'A'
或其他方面有所不同:'B' + 32 == 'b'
或'B' | 32 == 'b'
,其中|
是按位OR运算符。
答案 1 :(得分:1)
这称为位掩码。这里的检查器是位掩码。
第一个表达式:if((checker&(1<<val))>0)
获取该位,第二个表达式checker|=(1<<val)
设置该位。
左移操作符提高了2 ^ val。所以你有类似001000(对于'd')。
&amp;只要检查器的第i位和新的val(001000)都是1,运算符就返回true。因此,您知道该字符是否已被覆盖。
|运算符只是将第i位设置为1.因此,如果在某些情况下检查器是010000,现在它变为011000。