我正在研究一些代码检查字符串中字符的重复。以下是我在某处找到的答案。
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
,我的理解是1*2^(val)
。然后1 << val
需要=1
跳出循环。但是它取得了怎样的成果呢?感谢。
答案 0 :(得分:3)
让我们逐行分解。
val = s[j]-'a';
这是一个很好的技巧,可以将'a'->'z'
范围内的任何字符转换为数字0-25
。实际上,您通常将此视为s-'0'
将数字字符转换为数字,但它对于字母也同样有效。它利用了在ASCII / UTF8字符空间中字母字母是连续的这一事实,因此如果您将字符视为数字并减去起始字母,您将得到'a'
为0
的字符的“偏移量”。 {1}}和'z'
为25。
if ((checker & (1<<val)) >0) break;
这里的关键是要了解1<<val
将会做什么。这会将单个1
位val
位左移。因此,对于'a'
,您将获得0b1
,对于'b'
,您将获得'0b10'
,依此类推。实际上,它对一个32位整数的位进行单热编码。如果我们&
这个checker
这个值记录了我们已经看过的相同的一个热字段,那么当且仅当{>0
时,结果值将为checker
1}}在代表字母的位中包含1
。如果是这种情况,我们发现了一个副本,所以我们打破了。
checker |= 1 << val;
如果我们来到这里,则意味着checker
在该字母的位中没有1
。所以我们现在看到这封信,需要更新checker
。 |=
使用之前的val
1
将它始终精确地设置为{{1}},同时保持其他任何位不变。
答案 1 :(得分:2)
一块一块:
将val
设置为当前字符 - 'a'
,即'a'
提供0
,'z'
25
val = s[j]-'a';
检查检查器中的位:如果val
中已经设置了位checker
,则中断。这可以通过逻辑方式将值与位掩码进行对比;如果该位置位,则该值应为正(假设,假设)。
if ((checker & (1<<val)) >0) break;
否则,将位val
设置为1。
checker |= 1 << val;
代码做了很多假设;例如int
需要至少有26位,并且字符在&#39; a&#39;之外。 - &#39; z&#39;在字符串中可能会导致未定义的行为。
答案 2 :(得分:1)
代码的作者使用变量'checker'作为位掩码来记住他已经看过的字符。这一行:
val = s[j] - 'a';
将字符s [j]的ASCII值向下标准化为'a'的ASCII值。基本上,他正在弄清楚这个字符在小写字母字符[0,25]范围内的字母是哪一个:a是0,b是1,c是2,依此类推。
然后他正在检查这个位是否已经设置在'checker'中。他通过将标准化值左移1并将其与'检查器'进行“并”来完成此操作。如果未在'checker'中设置该位,则逐位AND将返回零并且循环将继续。如果设置了,那么AND将返回非零值并且他的测试将打破循环。
当未设置该位时,他然后在'checker'中设置与该位置对应的位。如果字符是'a',则设置最低有效位,'b'然后设置第二个最低有效位,依此类推或者通过'val'与现有的'检查器'左移1'。 / p>
PS - 他可以很容易地让'checker'成为一个包含26个字符的数组并完成:
char checker[26] = { 0 };
...
while(j < s.size() && !checker[s[j] - 'a'])
{
checker[s[j] - 'a'] = 1;
++j;
++count;
}
...
我相信你会明白这一点。这基本上就是他正在做的事情,但是将数组填充到一个位掩码而不是使用一些位操作。这样,他也可以通过将检查器设置为零来轻松清除设置位。
答案 3 :(得分:1)
您向我们展示的一段有趣的代码需要一些假设:
代码的作用是在checker变量中为它到目前为止找到的每个字符设置一个位(26个小写字符适合某些31/32位int,1位与一个字符相关联)。他最好使用一些uint32_t,顺便说一句。
通过减去&#39; a&#39;如果他的字符串保持假设1,则从当前字符获得值(0..25)。
if()表达式测试之前是否设置了该位,即之前是否出现过该字符。
无论在检查器中设置哪个位,它都是!= 0.如果假设1成立,则始终> 0.(无法到达第31位,这是符号位。)
答案 4 :(得分:0)
对于找到的每个字符,都标记从右到左开始的每一个检查器。假设如果在字符串中找到b,则设置右边的第二位..如果是c,则它是第三位...并且此checker
位掩码用于匹配后续字符。