C++位运算的启蒙

        之前一直对位运算十分畏惧,但是最近刷题的时候发现好的解法通通的离不开位运算,于是乎分析起了一位大佬的代码(梦璃夜·天星 LeetCode大佬的名字),题目如下:

给你一个由英文字母组成的字符串 s ,请你找出并返回 s 中的 最好 英文字母。返回的字母必须为大写形式。如果不存在满足条件的字母,则返回一个空字符串。

最好 英文字母的大写和小写形式必须 都 在 s 中出现。

英文字母 b 比另一个英文字母 a 更好 的前提是:英文字母表中,b 在 a 之 后 出现。

 

示例 1:

输入:s = "lEeTcOdE"
输出:"E"
解释:
字母 'E' 是唯一一个大写和小写形式都出现的字母。
示例 2:

输入:s = "arRAzFif"
输出:"R"
解释:
字母 'R' 是大写和小写形式都出现的最好英文字母。
注意 'A' 和 'F' 的大写和小写形式也都出现了,但是 'R' 比 'F' 和 'A' 更好。
示例 3:

输入:s = "AbCdEfGhIjK"
输出:""
解释:
不存在大写和小写形式都出现的字母。
 

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/greatest-english-letter-in-upper-and-lower-case
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

        一个简单的题目,硬算也是没问题的。但是,如果用上位运算,嘎嘎快了!WC

        上大佬的代码!

class Solution {
public:
    string greatestLetter(string s) {
        int64_t t = 0;
        for(char c: s) t |= 1ll << (c - 'A');
        t = t & (t >> ('a'-'A'));
        return t ? string(1, 63-__builtin_clzll(t)+'A') : "";
    }
};

        也就几行的代码,接下来,图文并茂按照自己的理解来解释下这些语句啥意思

        

int64_t t = 0;

        这个就没啥好说的,64位的一个数,内容如下:

0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0

       

 for(char c: s) t |= 1ll << (c - 'A');

         这句代码就比较关键了,他可分为四步走(个人想法):

        Step1: for循环里面的操作

                    将字符串S中的数据遍历一遍

        Step2: 对S遍历的同时,算出其与大写字母A的ASCLL码的差值

        Step3: 对值为1(可能说法有点问题)的64位long long 数进行左移 左移的位数就是Step2算的差值

         此时可以发现,如果说遍历的字母为A,那么他们的差值应该不会改变,所以也就不需要左移。

0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 1

上表表示已经识别了字母A,如果说遍历的字母是B,那么位表示便是:

0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 1 0

由此可以发现,此番操作后,遍历的字母基本可以按照zyx...cba...ZYX...CBA这样的表示了,是从大到小的一个顺序(按照ascll码)。

        Step4: 对进行左移了的数进行运算

0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 1 0

上表是假设遍历字母是B的位表示(自己这么叫的,我也不知道该叫啥),下表示初始值。

0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0

进行对应位或操作,容易看出,结果如下:

0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 1 0

t = t & (t >> ('a'-'A'));

这句代码就是一个求结果代码,分三步走:

        Step1: 首先求大小写字母他们的距离差值,应该是对应大小写(A与a)他们之间的位置差值为32.

        Step2: 对遍历完的数据进行右移操作,为了将小写字母移动到其对应的大写字母的对应位上。

        展示一个不是很恰当的例子:

z y x 0 0 0 Z Y X 0 0 u

                                                                        Table1

上表是原始数据,然后进行右移6位操作

0 0 0 0 0 0 z y x 0 0 0

                                                                        Table2

我们所举例子是移动6位便可以实现大小写字母位置对齐操作(小写字母对齐大写字母),代码里面计算的'a'-'A'也是一样的作用。

        Step3: 最后一步,进行与操作,判断是否存在大小写字母都存在的情况

                    如Table1和Table2所示,他们的与操作可以表示为:

0 0 0 0 0 0 1 1 1 0 0 0

                                                                        Table3

Table3中的1代表该位置的大小写字母均存在,最后一步便是求该位置的具体表示值了。

return t ? string(1, 63-__builtin_clzll(t)+'A') : "";

        如果t为0,那表示不存在大小写均存在的字母,则输出"";如果存在这么个都存在的情况,那么他们的对应位应该是1,接下来就是找这个1的位置了。

        __builtin_clzll()这个函数,可以返回从左往右数第一个1值得相对位置(对于最左边数据讲,也就是其左边有多少个0),对于Table3来说就是6。

        从上上一句代码的第2,3步可知,首先对原始数据进行了一个右移操作,那么其左边位置(高位)均为0,那部分位置包含小写字母的映射位置。即原来小写字母映射位置全部都为0了,然后这些小写字母的映射位置进行了偏移,偏到了大写字母的映射位置。最后数据应该就变成了像Table3那样了。

        至于代码中的63,则是对1位置与其所对应的字母之间关系的修正系数了。

Exam:

        一共有64位,小写字母z映射的位置为第58位(从右往左数),因为z的ascll码值为122,A为65,差值为57,但是下标是从1开始的,那么z对应的位置就是58。

        往右偏移32位后,z到了Z的初始映射位置,此时其下标应该是26,__builtin_clzll()函数返回的值就是38。这是要根据这个值来推算Z的值,以A为基准,如果要得到Z那么应该比A大25。

        所有应该有 n - 38 = 25,可以求得n =63。

Ps:写的不好的地方还请多多包容!

猜你喜欢

转载自blog.csdn.net/yoonaanna/article/details/127250794