LEETCODE 第 205 場周賽

LEETCODE 第 205 場周賽

A. 替換全部的問號

題目大意

修改字符串中的 ? 字符,使得字符串中不存在連續相同的字母c++

1 <= n <= 100算法

僅包含小寫字母和 ? 字符數組

思路分析

很暴力的題目,貪心選取就結束了ui

代碼

#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 。返回全部知足規則的三元組:spa

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

思路分析

其實相似於兩數之和,無非是 雙指針 或者利用 hashmap (因爲 n 的數量級爲 1e3, 因此直接暴力顯然是不合適的)指針

那麼思路就比較簡單了,還有一個坑點在於數組中最大的數字爲 1e5,平方以後沒法用 int 表示,因此須要開 long longcode

對於雙指針寫法,須要考慮如何處理重複元素,其實是一個簡單的組合數學知識,分爲兩種狀況:排序

  • nums2[j] == nums2[k], 由於咱們排序完成,因此表明 [j, k] 區間中,值都是相同的,且知足條件。根據組合數學能夠得知,可選組合爲 \(\binom{k - j + 1}{2}\)
  • 假如 nums2[j] != nums2[k],假設 j <= k,咱們分別往右往左進行掃描,獲得區間 [j, tj)(tk, k]。注意區間的閉開性。而後利用組合數學的乘法原理計算 \((tj - j) \times (k - tk)\) 。再進行指針的跳轉

而利用 hashmap 的方法則簡單許多,再也不贅述。ip

代碼

#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 的代價。leetcode

返回使字符串任意相鄰兩個字母不相同的最小刪除成本

請注意,刪除一個字符後,刪除其餘字符的成本不會改變。

  • 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));
    }
};
相關文章
相關標籤/搜索