球球速刷LC--位操作 二轮

异或

唯一数字!!!

所有数字异或后,出现两次的数字会变成0。结果就是只出现一次的那个数字

int singleNumber(int A[], int n) {
         int result = A[0];
        for(int i =1 ; i < n ;++i)
        {
            result = result^A[i];
        }
        return result;
}
唯一数2

把数字当成一个32bit的数组,记录每个bit上1总的出现次数。则对唯一出现一次的数在对应bit上
是否存在1,等于该bit上1出现的次数%3.

class Solution {
public:
    int singleNumber(vector<int>& nums) {
    //用一个32位数组分别记录每个bit上1出现的总次数
        vector<int> count(32,0);
        for(auto curr:nums){
           for(int i=0;i<count.size();++i){
               //不停右移,从而判断当前数的第i bit是否为1
               if((curr>>i)&1)count[i]++;
           }
        }

        int res=0;
        for(int i=0;i<count.size();++i){
            if(count[i]%3){ //唯一数上第i个bit上是否存在1
                res|=(1<<i);
            }
        }
       return res; 
    }
};
唯一数字3!!!

想到这个系列的第一个题,就是找出单个的只出现一次的字符。
这道题里,有两个数字只出现一次,且这两个数字不相同。如果我们能把这两个只出现一次的数字分开,再利用第一个题的思路,就能找处这两个只出现一次的数。
思路:把所有的数字进行一次异或,得到的是只出现了一次的两个数字的异或结果C。
这两个数字不等,因此他们的二进制必定至少1位不同,即异或结果C中为1的那位(一个数字的该位为1,另个数字的该位为0)。找出从右向左的第一个不同的位置(异或值为1的位置)。假设是
第i个bit。则根据这个bit是1还是0,我们可以把所有数字分成两堆,而这两个只出现一次的数字分别在这两堆中。因此我们可以利用第一题思路找到各自堆中只出现一次的数字。

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int res = 0;
        for (int n : nums) res ^= n; //res中包含的是两个只出现一次数字的异或结果
        
        int a = 0, b = 0;
        int mask = 1;
        while ((mask & res) == 0) mask <<= 1; //找到异或结果中为1的那个位,根据这个位可以把数字分成两类。只出现一次的数字分别在其中一类
        for (int n : nums) {
            if (n & mask) //把数字分成两类,并分别异或,从而找到各自类中只出现一次的那个数。
                a ^= n;
            else
                b ^= n;
        }
        return {a, b};
    }
};

DNA重复序列

由于总共只有四种字符,因此可以用两个bit表示所有字符,因此对于长度为10的串,可以使用20bit进行编码。相当于对字符串进行哈希。

class Solution {
inline unsigned int bitCode( char c) //利用数字编码存储字符串  标记字符串
{
        if(c  == 'A' )return  0;
        else  if(c == 'C' ) return 1;
        else  if(c == 'G' )return 2;
        else{
            return 3;
        }
}
public:
	vector<string> findRepeatedDnaSequences(string s) {
		/*思路   从左往右扫描长度为10的字符串  每次推进1个字符    如果该字符串没有在哈希表中出现
		则将该字符串作为关键字插入哈希表。 否则继续推进*/
		unordered_map<int, bool> hashMap;
		vector<string> result;
		int currCode = 0x00000000;
		//初始化字符串滑动窗	      	
	    if(s.size() <= 10) return result;
	    
	    for(int i = 0 ; i < 10 ; i++)
	    {
	        currCode <<=2;
	        currCode += bitCode(s[i]);
	    }
	    
	    currCode = currCode & 0x000FFFFF;
	    hashMap[currCode] = false ;
	  
	    for(int i = 10 ; i < s.size() ; ++i )
	    {
	        currCode <<=2;
	        currCode += bitCode(s[i]);
	        currCode &=  0x000FFFFF; //利用20个bit位  每2位代表一个字符来表示整个字符串
	        
            auto it=hashMap.find(currCode);
            if(it != hashMap.end()){
                if(it->second == false){
                    it->second=true;
                    result.push_back(s.substr(i-10+1,10));
                }
            }else{
                hashMap[currCode] = false;
            }
	    }
	
		return result;
	}
};
区间AND操作

由于只有所有数字在某bit为1,该bit最终相与后才能为1. 又因为数字是连续的,因此同为1的bit都在左侧。
如一个范围[26, 30],它们的二进制如下:

11010  11011  11100  11101  11110

我们只要写代码找到左边公共的部分即可。而较小的数m左边连续的1一定<n左边连续的1的个数。
因此,我们可以对m n每次向右移一位,直到m和n相等,记录下所有平移的次数i,然后再把m左移i位即为最终结果

class Solution {
public:
    int rangeBitwiseAnd(int m, int n) {
        int i = 0;
        while (m != n) {
            m >>= 1;
            n >>= 1;
            ++i;
        }
        return (m << i);
    }
};
bit1的个数

是DP与位操作结合的问题。
观察以下规律,可以看到某个数的1的个数=消除最左侧bit 1后得到的数里1的个数+1
因此可以使用DP进行递推。
其中消除最左侧1的个数可以通过记录≤当前数里最后一个2的N次方。从而进行取反相与操作。

0000 --》初始条件
0001 --》初始条件
0010 --> 消除bit 1 所得数字的1的个数+1–》消除2^1中的1
0011 --> 消除bit 1 所得数字的1的个数+1–》消除2^1中的1
0100 --> 消除bit 2 所得数字的1的个数+1–》消除2^2中的1
0101 --> 消除bit 2 所得数字的1的个数+1–》消除2^2中的1
0110 --> 消除bit 2 所得数字的1的个数+1–》消除2^2中的1
0111 --> 消除bit 2 所得数字的1的个数+1–》消除2^2中的1

class Solution {
public:
    vector<int> countBits(int num) {        
        vector<int>dp(num+1,0);//dp[i]为i数字中bit 1的数字个数
            
        dp.resize(num+1,0);
        dp[0]=0;
        if(num>=1) dp[1]=1;
        
        if(num<=1) return dp;
        
        int _2exp = 2;
                
        for(int i=2;i<=num;++i){
            if(i>=(_2exp<<1)){ //更新离当前数字i最接近且<i的2的整数幂数
                _2exp<<=1;
            }
           dp[i] = dp[(~(_2exp))&i]+1;//消除最左侧1.递推到子问题+1
        }
        return dp;
        
    }
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值