zoukankan      html  css  js  c++  java
  • LEETCODE 第 205 场周赛

    LEETCODE 第 205 场周赛

    A. 替换所有的问号

    题目大意

    修改字符串中的 ? 字符,使得字符串中不存在连续相同的字母

    1 <= n <= 100

    仅包含小写字母和 ? 字符

    思路分析

    很暴力的题目,贪心选取就结束了

    代码

    #define sz(x) ((int)x.size())
    
    class Solution {
    public:
        char get(int i, string s){
            char pre = i == 0 ? '$' : s[i - 1];
            char nxt = i == sz(s) - 1 ? '$' : s[i + 1];
            for (int i = 0; i < 26; ++ i)
                if (i + 'a' != pre && i + 'a' != nxt) return (i + 'a');
            return '$';
        }
    
        string modifyString(string s) {
            for (int i = 0; i < sz(s); ++ i) if (s[i] == '?') s[i] = get(i, s);
            return s;
        }
    };
    

    B. 数的平方等于两数乘积的方法数

    题目大意

    给定两个整数数组 nums1nums2 。返回所有满足规则的三元组:

    1. (nums[i] ^2 = nums[j] imes nums[k] quad i in [0, nums1.length],quad j,k in [0, nums2.length] quad j eq k)
    2. (nums[i] ^2 = nums[j] imes nums[k] quad i in [0, nums2.length],quad j,k in [0, nums1.length] quad j eq k)
    • 1 <= nums1.length, nums2.length <= 1000
    • 1 <= nums1[i], nums2[i] <= 10^5

    思路分析

    其实类似于两数之和,无非是 双指针 或者利用 hashmap (由于 n 的数量级为 1e3, 所以直接暴力显然是不合适的)

    那么思路就比较简单了,还有一个坑点在于数组中最大的数字为 1e5,平方之后无法用 int 表示,所以需要开 long long

    对于双指针写法,需要考虑如何处理重复元素,实际上是一个简单的组合数学知识,分为两种情况:

    • nums2[j] == nums2[k], 因为我们排序完成,所以代表 [j, k] 区间中,值都是相同的,且满足条件。根据组合数学可以得知,可选组合为 (inom{k - j + 1}{2})
    • 假如 nums2[j] != nums2[k],假设 j <= k,我们分别往右往左进行扫描,得到区间 [j, tj)(tk, k]。注意区间的闭开性。然后利用组合数学的乘法原理计算 ((tj - j) imes (k - tk)) 。再进行指针的跳转

    而利用 hashmap 的方法则简单许多,不再赘述。

    代码

    #define SORT(x) sort(x.begin(), x.end())
    #define sz(x) ((int)x.size())
    using VI = vector<int>;
    using LL = long long;
    
    class Solution {
    public:
        int help(VI nums1, VI nums2){
            int ans = 0;
            for (int i = 0; i < sz(nums1); ++ i){
                LL sq = 1LL * nums1[i] * nums1[i];
                int l = 0, r = sz(nums2) - 1;
                while (l < r){
                    LL cur = 1LL * nums2[l] * nums2[r];
                    if (cur == sq){
                        if (nums2[l] == nums2[r]){
                            ans += (r - l + 1) * (r - l) / 2;
                            break;
                        }    
                        int tl = l + 1, tr = r - 1;
                        while (tl < r && nums2[tl] == nums2[l]) ++ tl;
                        while (tr > l && nums2[tr] == nums2[r]) -- tr;
                        ans += (tl - l) * (r - tr);
                        l = tl - 1, r = tr + 1;
                        ++ l;
                    }
                    else if (cur < sq) ++ l;
                    else -- r;                
                }
            }
            return ans;
        }
    
        int numTriplets(vector<int>& nums1, vector<int>& nums2) {
            SORT(nums1);
            SORT(nums2);
            
            int res(0);
            res += help(nums1, nums2);
            res += help(nums2, nums1);
            return res;
        }
    };
    
    #define SORT(x) sort(x.begin(), x.end())
    #define sz(x) ((int)x.size())
    using VI = vector<int>;
    using LL = long long;
    
    class Solution {
    public:
        unordered_map<LL, int> mp1, mp2;
        int numTriplets(vector<int>& nums1, vector<int>& nums2) {
            mp1.clear(), mp2.clear();
            for (int x: nums1) ++ mp1[1LL * x * x];
            for (int x: nums2) ++ mp2[1LL * x * x];
    
            int ans(0);
            for (int i = 0; i + 1 < sz(nums1); ++ i){
                for (int j = i + 1; j < sz(nums1); ++ j){
                    ans += mp2[1LL * nums1[i] * nums1[j]];
                }
            }
            for (int i = 0; i + 1 < sz(nums2); ++ i){
                for (int j = i + 1; j < sz(nums2); ++ j){
                    ans += mp1[1LL * nums2[i] * nums2[j]];
                }
            }
    
            return ans;
        }
    };
    

    C. 避免重复字母的最小删除成本

    题目大意

    给你一个字符串 s 和一个整数数组 cost ,其中 cost[i] 是从 s 中删除字符 i 的代价。

    返回使字符串任意相邻两个字母不相同的最小删除成本

    请注意,删除一个字符后,删除其他字符的成本不会改变。

    • s.length == cost.length
    • 1 <= s.length, cost.length <= 10^5
    • 1 <= cost[i] <= 10^4
    • s 中只含有小写英文字母

    思路分析

    首先,不难想到使用 dp 去解决,重点在于 dp 数组如何表示。 这题我们可以顺序 dp ,重点在于考虑最后一个字符,因此 dp 数组可以表示为:

    dp[i][j] := 字符串前 i 个字符,且结尾字符为 j 且满足任意相邻字符不同的最小删除开销

    则转移有两种方式,第一种是不删当前字符,第二种是删除当前字符。设当前位置为 i ,当前字符为 c

    对于第一种转移,枚举 dp[i - 1][k]k != cdp[i][c] = min(dp[i][c], dp[i - 1][k])

    对于第二种转移,枚举 dp[i - 1][k]dp[i][k] = min(dp[i][k], dp[i - 1][k])

    当然,存在一个更简单的方法,寻找每一段重复区间,保留其中删除代价最大的字符,用滑动窗口解决。

    代码

    滑动窗口

    #define chmax(a, b) a = max(a, b);
    const int rinf = 0xc0c0c0c0;
    class Solution {
    public:
        int minCost(string s, vector<int>& cost) {
            int n = sz(cost);
            int idx = 0, ans = 0;
            while (idx < n){
                int sc = idx;
                int mx = rinf;
                int tot = 0;
                while (sc < n && s[sc] == s[idx]) {
                    chmax(mx, cost[sc]);
                    tot += cost[sc++];
                }
                idx = sc;
                ans += (tot - mx);
            }
            return ans;
        }
    };
    

    dp

    const int MAXN = 1e5 + 50;
    int dp[MAXN][30];
    
    class Solution {
    public:
        int minCost(string s, vector<int>& cost) {
            int n = s.length();
            for (int i = 0; i <= n; i++) for (int k = 0; k < 26; k++) dp[i][k] = -1;
            for (int k = 0; k < 26; k++) dp[0][k] = 0;
            
            for (int i = 1; i <= n; i++){
                int c = s[i - 1] - 'a', v = cost[i - 1];
                for (int k = 0; k < 26; k++){
                    if (k == c) continue;
                    if (dp[i - 1][k] == -1) continue;
                    if (dp[i][c] == -1 || dp[i][c] > dp[i - 1][k]) dp[i][c] = dp[i - 1][k];    
                }
                for (int k = 0; k < 26; k++){
                    if (dp[i - 1][k] == -1) continue;
                    if (dp[i][k] == -1 || dp[i][k] > dp[i - 1][k] + v) dp[i][k] = dp[i - 1][k] + v;
                }
            }
            
            int ans = -1;
            for (int k = 0; k < 26; k++){
                if (dp[n][k] == -1) continue;
                if (ans == -1 || ans > dp[n][k]) ans = dp[n][k];
            }
            
            return ans;
        }
    };		
    

    D. 保证图可完全遍历

    题目大意

    Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:

    • 类型 1:只能由 Alice 遍历。
    • 类型 2:只能由 Bob 遍历。
    • 类型 3:Alice 和 Bob 都可以遍历。

    给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 uivi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。

    返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。

    • 1 <= n <= 10^5
    • 1 <= edges.length <= min(10^5, 3 * n * (n-1) / 2)
    • edges[i].length == 3
    • 1 <= edges[i][0] <= 3
    • 1 <= edges[i][1] < edges[i][2] <= n
    • 所有元组 (typei, ui, vi) 互不相同

    思路分析

    首先,这个题目显然是一个图论题。要求每条路径都可以完全遍历,显然是生成树有关算法。如何保证保留的边最小呢?考虑贪心法。

    首先,公用边尽量保留。我们可以先利用公用边进行 DSU 的合并。随后,分别对于 Alice, Bob再运行生成树算法。保证每个人都可以遍历那么答案就非常简单了。

    代码

    namespace dsu1{
        vector<int> parent, rank;
         void initial(int k){
             parent.resize(k);
             rank.resize(k);
             for (int i = 0; i < k; ++ i) parent[i] = i, rank[i] = 0;
        }
        int find(int x){
            return parent[x] == x ? x : parent[x] = find(parent[x]); 
        }
        void to_union(int x, int y){ // x --> to, y --> from
            int tx = find(x), ty = find(y);
            if (tx == ty) return;
            if (rank[tx] > rank[ty]) parent[ty] = tx;
            else {
                parent[tx] = ty;
                if (rank[tx] == rank[ty]) ++ rank[ty];
            }
        }
        bool is_same(int x, int y){
            return find(x) == find(y);
        }
    };
    namespace dsu2{
        vector<int> parent, rank;
         void initial(int k){
             parent.resize(k);
             rank.resize(k);
             for (int i = 0; i < k; ++ i) parent[i] = i, rank[i] = 0;
        }
        int find(int x){
            return parent[x] == x ? x : parent[x] = find(parent[x]); 
        }
        void to_union(int x, int y){ // x --> to, y --> from
            int tx = find(x), ty = find(y);
            if (tx == ty) return;
            if (rank[tx] > rank[ty]) parent[ty] = tx;
            else {
                parent[tx] = ty;
                if (rank[tx] == rank[ty]) ++ rank[ty];
            }
        }
        bool is_same(int x, int y){
            return find(x) == find(y);
        }
    };
    namespace dsu3{
        vector<int> parent, rank;
         void initial(int k){
             parent.resize(k);
             rank.resize(k);
             for (int i = 0; i < k; ++ i) parent[i] = i, rank[i] = 0;
        }
        int find(int x){
            return parent[x] == x ? x : parent[x] = find(parent[x]); 
        }
        void to_union(int x, int y){ // x --> to, y --> from
            int tx = find(x), ty = find(y);
            if (tx == ty) return;
            if (rank[tx] > rank[ty]) parent[ty] = tx;
            else {
                parent[tx] = ty;
                if (rank[tx] == rank[ty]) ++ rank[ty];
            }
        }
        bool is_same(int x, int y){
            return find(x) == find(y);
        }
    };
    
    
    const int maxn = 1e5 + 50;
    class Solution {
    public:    
        int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
            dsu1::initial(n + 1), dsu2::initial(n + 1), dsu3::initial(n + 1);
            
            int cnt1, cnt2, cnt3, need(0), allu(0);
            cnt1 = cnt2 = cnt3 = n;
            for (auto &e:edges){
                int t = e[0], u = e[1], v = e[2];
                if (t == 1 || t == 3){
                    if (!dsu1::is_same(u, v)){
                        dsu1::to_union(u, v);
                        -- cnt1;
                    }
                }
                if (t == 2 || t == 3){
                    if (!dsu2::is_same(u, v)){
                        dsu2::to_union(u, v);
                        -- cnt2;
                    }
                }
                if (t == 3){
                    ++ allu;
                    if (!dsu3::is_same(u, v)){
                        dsu3::to_union(u, v);
                        -- cnt3;
                    }else ++ need;
                }
            }
            if (cnt1 != 1 || cnt2 != 1) return -1;
            return sz(edges) - (allu - need + 2 * (cnt3 - 1));
        }
    };
    
  • 相关阅读:
    Codeforces 177G2 Fibonacci Strings KMP 矩阵
    Codeforces Gym100187C Very Spacious Office 贪心 堆
    Codeforces 980F Cactus to Tree 仙人掌 Tarjan 树形dp 单调队列
    AtCoder SoundHound Inc. Programming Contest 2018 E + Graph (soundhound2018_summer_qual_e)
    BZOJ3622 已经没有什么好害怕的了 动态规划 容斥原理 组合数学
    NOIP2016提高组Day1T2 天天爱跑步 树链剖分 LCA 倍增 差分
    Codeforces 555C Case of Chocolate 其他
    NOIP2017提高组Day2T3 列队 洛谷P3960 线段树
    NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp
    NOIP2017提高组Day1T3 逛公园 洛谷P3953 Tarjan 强连通缩点 SPFA 动态规划 最短路 拓扑序
  • 原文地址:https://www.cnblogs.com/Last--Whisper/p/13632662.html
Copyright © 2011-2022 走看看