子域名访问计数&&比特位计数

111 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

[811. 子域名访问计数]

leetcode.cn/problems/su…

image-20221005145700336


题目是想要我们统计所有域名及其出现的次数,我们 自然就联想到映射,然后二者组合返回

我们可以发现: rep d1.d2.d3 其中req表示访问域名的次数, d1.d2.d3 d2.d3 d3都是域名,只不过他们的层级不相同而已, 并且访问次数和域名是以空格区分的,域名和域名是以.区分的

所以做法为:遍历计数配对域名数组,先根据空格的位置,记录访问次数, 然后划分域名,对应域名累加访问次数

class Solution {
public:
    vector<string> subdomainVisits(vector<string>& cpdomains) {
        unordered_map<string,int> map;//域名-访问次数
​
        //遍历计数配对域名数组
        for(auto& str:cpdomains)
        {
            //根据空格区分访问次数和域名
            int index = str.find(' ');
            //注意:atoi的参数是const char*  而str.substr(0,index)返回的是string
            //当然也可以使用:stoi函数,作用也是字符串转为整形,参数是string
            int times = stoi(str.substr(0,index));
            //int times = atoi(str.substr(0,index).c_str());//访问次数,字符串->整数
​
            string dmName = str.substr(index+1);//index往后的就是域名
            //划分域名,统计每个域名的访问次数
            while(index > 0)
            {
                map[dmName] += times;
                //划分下一级的域名
                //discuss.leetcode.com -> leetcode.com ->com ->跳出循环
                index = dmName.find('.');
                dmName = dmName.substr(index+1);
            }
        }
        //遍历哈希表 存放结果
        vector<string> ans; 
        for(auto& kv:map)
        {
            ans.push_back(to_string(kv.second) + " " + kv.first); //访问次数 空格 域名
        }
        return ans;
    }
};

需要注意atoi函数和stoi函数的区别!

  • 共同点:都是c++的字符处理函数,把字符串转化为整形

  • 区别:atoi()的参数是const char *,因此对于string类型的字符串str我们必须调用c_str()的方法把这个string 转换成const char *类型的

    • stoi()的参数是const string&

[338. 比特位计数]

leetcode.cn/problems/co…

image-20221005151928451


做法1:暴力做法 直接求0~n的每个数的比特位为1的情况放到数组中

class Solution {
public:
    //计算n中比特位为1的个数
    int CountBit(int n)
    {
        int count = 0;
        while(n)
        {
            count++;
            n&=(n-1);
        }
        return count;
    }
    /*
    int CountBit(int n)
    {
        int count = 0;
        while(n) count+=n&1,n>>=1;
        return count;
    }
    */
    vector<int> countBits(int n) {
        vector<int> ans(n+1,0);
        for(int i = 0;i<=n;i++)
        {
            ans[i] = CountBit(i);
        }
        return ans;
    }
};

做法2:观察规律

dp[i]的含义是:i的比特位当中1的个数

对于所有的数字,无非就是奇数和偶数,假设当前的数为i

  • 奇数: i的二进制表示当中,一定比它前面的i-1的那个偶数多一个1,多的就是最低位的比特位1
  • 偶数:i的二进制表示当中,比特位1的个数一定和i/2这个数的比特位1的个数是一样的, 因为偶数最低位肯定是0, /2就是右移一位.也就是把0抹掉,1的个数是不变的

所以我们可以得到下面的状态方程:

当i为奇数时: dp[i] = dp[i-1]+1

当i为偶数时: dp[i] = dp[i/2]


class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> res(n+1,0);
        for(int i = 0;i<=n;i++)
        {
            if(i&1) //i是奇数
                res[i] = res[i-1]+1;
            else    //i是偶数
                res[i] = res[i/2];
        }
        return res;
    }
};

实际上,上述的方程可以进一步优化:

  • 当i为奇数的时候,i-1为偶数, 其二进制中1的个数一定和它/2之后的个数一样, dp[i-1] = dp[(i-1)/2]
  • 因为i为奇数,所以 (i-1)/2 == i/2

所以转移方程可以是: dp[i/2] + i%2 如果i为奇数:i%2 == 1 i为偶数:i%2 == 0

其中i/2  == i >>1   i%2 == i&1


class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> res(n+1,0);
        for(int i = 0;i<=n;i++)
        {
            //res[i] = res[i/2] + i%2;
            res[i] = res[i>>1] + (i&1);
        }
        return res;
    }
};