[英]Bit manipulation with left shift and check string
我正在研究一些代码来检查字符串中字符的重复。 这是我在某处找到的一些答案。
int checker = 0, val =0, max = 0, j =0, count = 0;
for(int i=0; i<s.size() && j<s.size(); i++)
{
j = i;
while(j<s.size())
{
val = s[j]-'a';
if ((checker & (1<<val)) >0) break;
checker |= 1 << val;
j++;
count++;
}
if(count > max) max = count;
checker = 0;
count = 0;
}
return max;
该方法明确而直接。 但是,我对两行感到困惑。
val = s[j]-'a';
if ((checker & (1<<val)) >0) break;
checker |= 1 << val;
我不知道val在减去后是否有价值。 那么(1 << val)
是1
左移val,我的理解是1*2^(val)
。 然后1 << val
需要=1
才能跳出循环。 但是,请问是如何实现的呢? 谢谢。
让我们逐行将其分解。
val = s[j]-'a';
这是一个巧妙的技巧,它将把'a'->'z'
范围内'a'->'z'
任何字符转换为数字0-25
。 实际上,您通常将其视为s-'0'
以将数字字符转换为数字,但是它对于字母也同样有效。 它利用了以下事实:在ASCII / UTF8字符空间中,字母是连续的,因此,如果将字符视为数字并减去起始字母,则会得到字符的“偏移”,其中'a'
为0
和'z'
是25。
if ((checker & (1<<val)) >0) break;
关键是要了解1<<val
会做什么。 左移单个1
位val
位。 因此,对于'a'
您将获得0b1
;对于'b'
您将得到'0b10'
,依此类推。 有效地,它一次热编码一个字母到一个32位整数中的一位。 如果我们再&
这个蒙山我们的checker
值,它记录的,我们已经看到信件的同一个热位字段,所产生的价值将是>0
当且仅当checker
含有1
在代表字母位。 如果是这样,我们发现了一个重复项,所以我们中断了。
checker |= 1 << val;
如果我们到了这里,这意味着checker
的那个字母中没有1
。 因此,我们现在已经看到了这封信,并且需要更新checker
。 |=
从之前的val
进行修改将始终将该单个位精确设置为1
,而其他所有位均保持不变。
逐段:
设置val
当前字符- 'a'
这意味着, 'a'
给0
, 'z'
25
val = s[j]-'a';
检查检查了一下:如果该位val
在已经设置checker
,再突破。 这是通过将值与位掩码进行逻辑和运算来实现的; 如果该位置1,则该值应为正(假设,假设)。
if ((checker & (1<<val)) >0) break;
否则通过对bit val
进行设置来将其设置为1。
checker |= 1 << val;
该代码有很多假设; 例如int
至少需要26位,并且字符串中'a'-'z'之外的字符可能会导致不确定的行为。
代码的作者使用变量“ checker”作为位掩码来记住他已经看到的字符。 该行:
val = s[j] - 'a';
正在将字符s [j]的ASCII值向下按ASCII值“ a”进行归一化。 基本上,他正在弄清楚此字符在小写字母字符的范围[0,25]中是哪个字母:a为0,b为1,c为2,依此类推。
然后,他正在检查此位是否已在“检查器”中设置。 他通过左移1归一化的值并将其与“ checker”进行“与”操作来实现。 如果未在“检查器”中设置该位,则按位与将返回零,并且循环将继续。 如果已设置,则AND将返回非零值,并且他的测试将中断循环。
如果未设置该位,则他将在“ checker”中设置与该位置相对应的位。 如果字符是“ a”,则设置最低有效位,设置“ b”,然后设置第二个最低有效位,依此类推,方法是将现有的“ checker”按左移1个“ val”进行按位“或”运算。
PS-他可以很容易地将“ checker”设置为26个字符的数组并完成:
char checker[26] = { 0 };
...
while(j < s.size() && !checker[s[j] - 'a'])
{
checker[s[j] - 'a'] = 1;
++j;
++count;
}
...
我相信你会理解的。 基本上这就是他正在做的,但是正在将数组填充到位掩码中,而不是使用一些位操作。 这样,他还可以简单地通过将Checker设置为零来轻松清除设置的位。
您展示给我们的有趣的代码有一些假设:
代码要做的是为到目前为止找到的每个字符在checker变量中设置一个位(26个小写字符适合一些31/32位int,其中1个位与一个字符相关联)。 他最好使用一些uint32_t,顺便说一句。
通过从当前字符中减去“ a”,如果他的字符串符合假设1,他将获得值(0..25)。
if()表达式测试该位是否已经设置过,即该字符是否在之前设置过。
无论在checker中设置的哪个位,它都是!=0。并且如果假设1成立,则它始终>0。(无法到达符号位bit31。)
从找到的每个字符都标记了从右到左开始的每一个检查器。 可以说如果在字符串中找到b,则设置右边的第二位。如果它的c,则它是第三位...并且此checker
位掩码用于匹配后续字符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.