左移和检查字符串的位操作

时间:2015-02-13 19:28:52

标签: c++ c bit-manipulation

我正在研究一些代码检查字符串中字符的重复。以下是我在某处找到的答案。

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跳出循环。但是它取得了怎样的成果呢?感谢。

5 个答案:

答案 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将会做什么。这会将单个1val位左移。因此,对于'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)

您向我们展示的一段有趣的代码需要一些假设:

  1. 字符串s仅包含小写字母(&#39; a&#39; ...&#39; z&#39;)。
  2. int类型有32位(或更多)。
  3. 代码的作用是在checker变量中为它到目前为止找到的每个字符设置一个位(26个小写字符适合某些31/32位int,1位与一个字符相关联)。他最好使用一些uint32_t,顺便说一句。

    通过减去&#39; a&#39;如果他的字符串保持假设1,则从当前字符获得值(0..25)。

    if()表达式测试之前是否设置了该位,即之前是否出现过该字符。

    无论在检查器中设置哪个位,它都是!= 0.如果假设1成立,则始终> 0.(无法到达第31位,这是符号位。)

答案 4 :(得分:0)

对于找到的每个字符,都标记从右到左开始的每一个检查器。假设如果在字符串中找到b,则设置右边的第二位..如果是c,则它是第三位...并且此checker位掩码用于匹配后续字符。