ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LeetCode第68场双周赛

2021-12-26 13:34:48  阅读:209  来源: 互联网

标签:10 cnt 乘积 int res 双周 mp1 68 LeetCode


T1 5946. 句子中的最多单词数

题目描述:给你\(n\)个句子,求出含有最多单词的句子所含有的单词数

思路:根据题意模拟即可

时间复杂度:\(O(\sum|S|)\)

参考代码:

class Solution {
public:
    int mostWordsFound(vector<string>& sentences) {
        int res = 0;
        string t;
        for(auto s : sentences){
            stringstream text(s);
            int cnt = 0;
            while(text >> t) ++cnt;
            res = max(res , cnt);
        }
        return res;
    }
};

T2 5947. 从给定原材料中找到所有可以做出的菜

题目描述:你有\(n\)道不同菜的信息。给你一个字符串数组\(recipes\) 和一个二维字符串数组\(ingredients\)。第\(i\)道菜的名字为 \(recipes[i]\),如果你有它所有的原材料\(ingredients[i]\),那么你可以做出这道菜。一道菜的原材料可能是 另一道 菜也就是说 \(ingredients[i]\)可能包含\(recipes\) 中另一个字符串。同时给你一个字符串数组 \(supplies\),它包含你初始时拥有的所有原材料,每一种原材料你都有无限多。请你返回你可以做出的所有菜。你可以以 任意顺序 返回它们。

思路:比较明显的拓扑关系,考虑用map将菜品名称映射成点然后建图跑拓扑即可

时间复杂度:\(O(n)\)

参考代码:

class Solution {
public:
    vector<string> findAllRecipes(vector<string>& recipes, vector<vector<string>>& ingredients, vector<string>& supplies) {
        vector<string> res;
        vector<int>income(1000, 0);
        map<string, int> mp1;
        map<int, string> mp2;
        int cnt = 0;
        vector<vector<int>>graph(1000);
        int n = recipes.size();
        for (auto s : recipes) {
            if (mp1[s]) continue;
            mp1[s] = ++cnt;
            mp2[cnt] = s;
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < ingredients[i].size(); ++j) {
                string s = ingredients[i][j];
                if (mp1[s] == 0) {
                    mp1[s] = ++cnt;
                    mp2[cnt] = s;
                }
                int dx = mp1[s];
                graph[dx].push_back(mp1[recipes[i]]);
                income[mp1[recipes[i]]] ++;
            }
        }
        vector<int>vis(cnt + 1, 0);
        queue<int> q;
        for (auto s : supplies) {
            if (mp1[s] == 0) continue;
            int dx = mp1[s];
            vis[dx] = true;
            q.push(dx);
        }
        while (!q.empty()) {
            int ver = q.front(); q.pop();
            for (auto g : graph[ver]) {
                if (vis[g]) continue;
                if (--income[g] == 0) {
                    vis[g] = true;
                    q.push(g);
                }
            }
        }
        for (auto s : recipes) {
            if (vis[mp1[s]]) res.push_back(s);
        }
        return res;
    }
};

T3 5948. 判断一个括号字符串是否有效

题目描述:给你两个串\(s\)与\(t\),\(s\)串只包含\('('\)与\(')'\),\(t\)串只包含\(0 , 1\),其含义是:如果是\(0\)表示\(s\)串对应位上的字符可以更改,否则不可更改,判断\(s\)串是否是合法的括号序列。

思路:考虑将\(t_i = 0\)的位置作为通配符,定义\(minCntleft\)表示当前最少的左括号,\(maxCntleft\)表示当前最多的左括号,每次遇到\(t_i = 1\)的位置就将二者都\(+1\)或\(-1\),遇到可以更改的地方,就将\(maxCntleft + 1\) ,并将\(minCntleft - 1\),注意最少的左括号数应当不小于\(0\),同时\(maxCntleft\)在任意时刻都必须非负。最终判断\(minCntleft\)是否等于\(0\)即可。

时间复杂度:\(O(n)\)

参考代码:

class Solution {
private:
public:
    bool canBeValid(string s, string t) {
        int n = s.size();
        if(n % 2 == 1) return false;
        int minCntleft = 0 , maxCntleft = 0;
        for(int i = 0 ; i < n ; ++i){
            if(s[i] == '(' && t[i] == '1') ++minCntleft, ++maxCntleft;
            else if(s[i] == ')' && t[i] == '1') --minCntleft , --maxCntleft;
            else --minCntleft, ++maxCntleft;
            minCntleft = max(0 , minCntleft);
            if(maxCntleft < 0) return false;
        }
        return minCntleft == 0;
    }
};

T4 5949. 一个区间内所有数乘积的缩写

题目描述:给你两个正整数 \(left\) 和 \(right\) ,满足 \(left \leq right\) 。请你计算 闭区间$ [left, right]$ 中所有整数的 乘积 。由于乘积可能非常大,你需要将它按照以下步骤 缩写 :统计乘积中 后缀 \(0\) 的数目,将这个数目记为$ C$ 。
比方说,\(1000\)中有\(3\)个后缀 \(0\) ,\(546\) 中没有后缀 \(0\) 。将乘积中剩余数字记为\(d\) 。如果 \(d > 10\) ,那么将乘积表示为 \(<pre>...<suf>\)的形式,其中 \(<pre>\) 表示乘积最 开始 的 \(5\)个数位,\(<suf>\)表示删除后缀 \(0\) 之后 结尾的\(5\) 个数位。如果 \(d \leq 10\) ,我们不对它做修改。比方说,我们将 \(1234567654321\) 表示为 \(12345...54321\) ,但是 \(1234567\) 仍然表示为\(1234567\) 。最后,将乘积表示为 字符串 \(''<pre>...<suf>eC''\) 。比方说,\(12345678987600000\)被表示为 \(''12345...89876e5''\) 。请你返回一个字符串,表示 闭区间 \([left, right]\) 中所有整数 乘积 的 缩写 。

思路:首先考虑尾数\(C\),考虑求出\([left , right]\)内每个数含有因子\(2\)和因子\(5\)的个数,二者中的较小者即为\(C\);对于尾数考虑将\([left , right]\)之间的数字全剔除掉因子\(2\)和\(5\),然后构成一个新的数组,这些数字的乘积再乘以多剔除的\(2\)或\(5\)就是乘积中不含尾数\(0\)的部分,我们只需要在计算过程中对该乘积模\(1e5\)即可;对于前缀部分,考虑在计算乘积过程中保留高\(12\)位(理论上不正确),然后再最后再保留高\(5\)位;判断乘积是否超过\(10\)位可以在计算前缀部分的时候判断一下即可。

可以参考大佬写的题解 :拆一个区间内所有数乘积的缩写

时间复杂度:\(O(nlogn)\)

参考代码:

class Solution {
private:
    string cal(vector<int>& nums, int cnt2, int cnt5) {
        long long res = 1, ans = 1;//前缀 后缀
        const int mod = 1e5;//模
        bool flag = false;//标记成绩长度是否超过10位
        int cnt = 5;//前缀保留过程中需要进行的除法次数至少为5
        //计算数组内的元素的成绩
        for (auto num : nums) {
            res *= num;
            ans *= num;
            ans %= mod;
            if (res >= 1e10)flag = true;//乘积超过了10位
            while (res >= 1e12)res /= 10, --cnt;//保留高12位 并对除法计数次数-1
        }
        //乘上多剔除的2
        for (int i = 1; i <= cnt2; ++i) {
            res *= 2;
            ans *= 2;
            ans %= mod;
            if (res >= 1e10)flag = true;
            while (res >= 1e12)res /= 10, --cnt;
        }
        //乘上多剔除的5
        for (int i = 1; i <= cnt5; ++i) {
            res *= 5;
            ans *= 5;
            ans %= mod;
            if (res >= 1e10)flag = true;
            while (res >= 1e12)res /= 10, --cnt;
        }
        //前缀只保留5位
        while (res >= 100000) res /= 10, --cnt;
        //如果还没除到5次 还需要继续除
        while (cnt > 0) res /= 10, --cnt;
        string t1 = to_string(res), t2 = to_string(ans);
        if (flag) {//如果长度超过10
            while (t2.size() < 5) t2 = '0' + t2;//需要对后缀补前导0
            return t1 + "..." + t2;
        }
        //长度小于10
        else if (res == 0) return to_string(ans);//前缀等于0
        //前缀不等于0 就需要对后缀补前导0
        while (t2.size() < 5) t2 = '0' + t2;
        return t1 + t2;
    }
public:
    string abbreviateProduct(int left, int right) {
        //将区间转化成数组,并剔除因子2 和 5
        vector<int> nums;
        int cnt2 = 0, cnt5 = 0;
        for (int i = left; i <= right; ++i) {
            int j = i;
            while (j % 2 == 0) ++cnt2, j /= 2;
            while (j % 5 == 0) ++cnt5, j /= 5;
            nums.push_back(j);
        }
        //0的个数等于 2 和 5中的较小者
        int C = min(cnt2, cnt5);
        //计算前面的部分
        string res = cal(nums, cnt2 - C, cnt5 - C);
        return res + "e" + to_string(C);
    }
};

标签:10,cnt,乘积,int,res,双周,mp1,68,LeetCode
来源: https://www.cnblogs.com/cherish-/p/15732738.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有