• 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));
        }
    };
    
  • 相关阅读:
    laravel安装
    redis缓存设置和读取
    window下装redis扩展(以php5.5为例)
    静态缓存
    原生js发送ajax请求
    数据库查询语句面试
    Cookie与Session
    面试题-一个for循环输出一个棱形
    编程题:利用for循环打印 9*9 表?
    java面试题之分析(二)
  • 原文地址:https://www.cnblogs.com/Last--Whisper/p/13632662.html
Copyright © 2020-2023  润新知