1. 程式人生 > 實用技巧 >LeetCode-第 198 場周賽

LeetCode-第 198 場周賽

5464.換酒問題

題目連結:5464.換酒問題

小區便利店正在促銷,用 numExchange 個空酒瓶可以兌換一瓶新酒。你購入了 numBottles 瓶酒。

如果喝掉了酒瓶中的酒,那麼酒瓶就會變成空的。

請你計算 最多 能喝到多少瓶酒。

樣例輸入與樣例輸出 Sample Input and Sample Output

示例 1:

輸入: numBottles = 9, numExchange = 3
輸出: 13
解釋: 你可以用 3 個空酒瓶兌換 1 瓶酒。
所以最多能喝到 9 + 3 + 1 = 13 瓶酒。

示例 2:

輸入: numBottles = 15, numExchange = 4
輸出:

19
解釋: 你可以用 4 個空酒瓶兌換 1 瓶酒。
所以最多能喝到 15 + 3 + 1 = 19 瓶酒。

示例 3:

輸入: numBottles = 5, numExchange = 5
輸出: 6

示例 4:

輸入: numBottles = 2, numExchange = 3
輸出: 2

提示 Hint

提示:

  • 1 <= numBottles <= 100
  • 2 <= numExchange <= 100

題解

暴力模擬即可。

class Solution {
 public:
  int closestToTarget(vector<int>& arr, int target) {
    set<int>a[2];
    a[0].insert(INT_MAX);
    int ans(INT_MAX);
    for(int i = 0; i < arr.size(); ++i) {
      a[(i + 1) % 2].clear();
      for(int j : a[i % 2]) {
        a[(i + 1) % 2].insert(j & arr[i]);
        ans = min(ans, abs((j & arr[i]) - target));
      }
      a[(i + 1) % 2].insert(arr[i]);
      ans = min(ans, abs(arr[i] - target));
      a[i % 2].clear();
    }
    return ans;
  }
};

5465.子樹中標籤相同的節點數

題目連結:5465.子樹中標籤相同的節點數

給你一棵樹(即,一個連通的無環無向圖),這棵樹由編號從 0n - 1 的 n 個節點組成,且恰好有 n - 1edges
。樹的根節點為節點 0 ,樹上的每一個節點都有一個標籤,也就是字串 labels 中的一個小寫字元(編號為 i 的 節點的標籤就是
labels[i]

邊陣列 edgesedges[i] = [ai, bi] 的形式給出,該格式表示節點 aibi 之間存在一條邊。

返回一個大小為 n 的陣列,其中 ans[i] 表示第 i 個節點的子樹中與節點 i 標籤相同的節點數。

T

中的子樹是由 T 中的某個節點及其所有後代節點組成的樹。

樣例輸入與樣例輸出 Sample Input and Sample Output

示例 1:

輸入: n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], labels = "abaedcd"
輸出: [2,1,1,1,1,1,1]
解釋: 節點 0 的標籤為 'a' ,以 'a' 為根節點的子樹中,節點 2 的標籤也是 'a' ,因此答案為 2 。注意樹中的每個節點都是這棵子樹的一部分。
節點 1 的標籤為 'b' ,節點 1 的子樹包含節點 1、4 和 5,但是節點 4、5 的標籤與節點 1 不同,故而答案為 1(即,該節點本身)。

示例 2:

輸入: n = 4, edges = [[0,1],[1,2],[0,3]], labels = "bbbb"
輸出: [4,2,1,1]
解釋: 節點 2 的子樹中只有節點 2 ,所以答案為 1 。
節點 3 的子樹中只有節點 3 ,所以答案為 1 。
節點 1 的子樹中包含節點 1 和 2 ,標籤都是 'b' ,因此答案為 2 。
節點 0 的子樹中包含節點 0、1、2 和 3,標籤都是 'b',因此答案為 4 。

示例 3:

輸入: n = 5, edges = [[0,1],[0,2],[1,3],[0,4]], labels = "aabab"
輸出: [3,2,1,1,1]

示例 4:

輸入: n = 6, edges = [[0,1],[0,2],[1,3],[3,4],[4,5]], labels = "cbabaa"
輸出: [1,2,1,1,2,1]

示例 5:

輸入: n = 7, edges = [[0,1],[1,2],[2,3],[3,4],[4,5],[5,6]], labels = "aaabaaa"
輸出: [6,5,4,1,3,2,1]

提示 Hint

提示:

  • 1 <= n <= 10^5
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • labels.length == n
  • labels 僅由小寫英文字母組成

題解

DFS 遍歷即可。

class Solution {
 public:
  vector<int> DFS(int root, int fa, vector<int>&ans, const vector<vector<int>>&g, const string &labels) {
    vector<int>cnt(26, 0);
    cnt[labels[root] - 'a']++;
    for(int v : g[root]) {
      if(v == fa)
        continue;
      vector<int>delta = DFS(v, root, ans, g, labels);
      for(int j = 0; j < 26; ++j)
        cnt[j] += delta[j];
    }
    ans[root] = cnt[labels[root] - 'a'];
    return cnt;
  }

  vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
    if(n == 0)
      return {};
    vector<int>ans(n);
    vector<vector<int>>g(n);
    for(vector<int>edge : edges) {
      g[edge[0]].push_back(edge[1]);
      g[edge[1]].push_back(edge[0]);
    }
    DFS(0, -1, ans, g, labels);
    return ans;
  }
};

5466.最多的不重疊子字串

題目連結:5466.最多的不重疊子字串

給你一個只包含小寫字母的字串 s ,你需要找到 s 中最多數目的非空子字串,滿足如下條件:

  1. 這些字串之間互不重疊,也就是說對於任意兩個子字串 s[i..j]s[k..l] ,要麼 j < k 要麼 i > l
  2. 如果一個子字串包含字元 c ,那麼 s 中所有 c 字元都應該在這個子字串中。

請你找到滿足上述條件的最多子字串數目。如果有多個解法有相同的子字串數目,請返回這些子字串總長度最小的一個解。可以證明最小總長度解是唯一的。

請注意,你可以以 任意 順序返回最優解的子字串。

樣例輸入與樣例輸出 Sample Input and Sample Output

示例 1:

輸入: s = "adefaddaccc"
輸出: ["e","f","ccc"]
解釋: 下面為所有滿足第二個條件的子字串:
[
"adefaddaccc"
"adefadda",
"ef",
"e",
"f",
"ccc",
]
如果我們選擇第一個字串,那麼我們無法再選擇其他任何字串,所以答案為 1 。如果我們選擇 "adefadda" ,剩下子字串中我們只可以選擇 "ccc" ,它是唯一不重疊的子字串,所以答案為 2 。同時我們可以發現,選擇 "ef" 不是最優的,因為它可以被拆分成 2 個子字串。所以最優解是選擇 ["e","f","ccc"] ,答案為 3 。不存在別的相同數目子字串解。

示例 2:

輸入: s = "abbaccd"
輸出: ["d","bb","cc"]
解釋: 注意到解 ["d","abba","cc"] 答案也為 3 ,但它不是最優解,因為它的總長度更長。

提示 Hint

提示:

  • 1 <= s.length <= 10^5
  • s 只包含小寫英文字母。

題解

找到所有子串,排序(短的優先),能加則加。

typedef pair<int, int>PII;

class Solution {
 public:
  vector<string> maxNumOfSubstrings(string s) {
    if(s.length() == 0)
      return {};
    vector<PII>st;
    vector<int>first_pos(26, -1), last_pos(26, -1);
    for(int i = 0; i < 26; ++i) {
      first_pos[i] = s.find_first_of('a' + i);
      last_pos[i] = s.find_last_of('a' + i);
    }
    for(int i = 0; i < 26; ++i) {
      size_t b = s.find_first_of('a' + i);
      size_t e = s.find_last_of('a' + i);
      if(b == string::npos)
        continue;
      for(bool redo = true; redo;) {
        redo = false;
        for(int j = b; j <= e; j++)
          if(e < last_pos[s[j] - 'a'])
            e = last_pos[s[j] - 'a'];
          else if(b > first_pos[s[j] - 'a'])
            b = first_pos[s[j] - 'a'], redo = true;
      }
      st.push_back({e - b + 1, b});
    }
    sort(st.begin(), st.end());
    vector<bool>vis(s.length(), false);
    vector<string>ans;
    for(PII p : st) {
      bool v = false;
      for(int j = p.second; j < p.second + p.first; j++) {
        if(vis[j]) {
          v = true;
          break;
        }
      }
      if(v)
        continue;
      ans.push_back(s.substr(p.second, p.first));
      for(int j = p.second; j < p.second + p.first; j++)
        vis[j] = true;
    }
    return ans;
  }
};

5467.找到最接近目標值的函式值

題目連結:5467.找到最接近目標值的函式值

Winston 構造了一個如上所示的函式 func 。他有一個整數陣列 arr 和一個整數 target ,他想找到讓 |func(arr, l, r) - target| 最小的 lr

請你返回 |func(arr, l, r) - target| 的最小值。

請注意, func 的輸入引數 lr 需要滿足 0 <= l, r < arr.length

樣例輸入與樣例輸出 Sample Input and Sample Output

示例 1:

輸入: arr = [9,12,3,7,15], target = 5
輸出: 2
解釋: 所有可能的 [l,r] 數對包括 [[0,0],[1,1],[2,2],[3,3],[4,4],[0,1],[1,2],[2,3],[3,4],[0,2],[1,3],[2,4],[0,3],[1,4],[0,4]], Winston 得到的相應結果為 [9,12,3,7,15,8,0,3,7,0,0,3,0,0,0] 。最接近 5 的值是 7 和 3,所以最小差值為 2 。

示例 2:

輸入: arr = [1000000,1000000,1000000], target = 1
輸出: 999999
解釋: Winston 輸入函式的所有可能 [l,r] 數對得到的函式值都為 1000000 ,所以最小差值為 999999 。

示例 3:

輸入: arr = [1,2,4,8,16], target = 0
輸出: 0

提示 Hint

提示:

  • 1 <= arr.length <= 10^5
  • 1 <= arr[i] <= 10^6
  • 0 <= target <= 10^7

題解

法1.簡單的ST表預處理+二分答案

class Solution {
 public:
  int gao(vector<vector<int>>&d, int x, int y)const {
    int k = log2(y - x + 1);
    int ret = d[x][k] & d[y - (1 << k) + 1][k];
    return ret;
  }

  int closestToTarget(vector<int>& arr, int target) {
    int len = arr.size();
    vector<vector<int>>d(len, vector<int>(20));
    for(int i = 0; i < len; ++i)
      d[i][0] = arr[i];
    for(int j = 1; j <= log2(len); ++j) {
      for(int i = 0; i + (1 << j) - 1 < len; ++i) {
        d[i][j] = d[i][j - 1] & d[i + (1 << (j - 1))][j - 1];
      }
    }
    int ans(0x7ffffff);
    for(int i = 0; i < len; ++i) {
      if(i && arr[i] == arr[i - 1])
        continue;
      int l = i, r = len - 1, mid;
      if(gao(d, i, r) < target)
        while(l < r) {
          //cout<< l << " " << r<<endl;
          mid = (l + r) >> 1;
          if(gao(d, i, mid) > target)
            l = mid + 1;
          else
            r = mid;
        } else
        l = r;
      ans = min(ans, abs(gao(d, i, l) - target));
      if(l - 1 >= i)
        ans = min(ans, abs(gao(d, i, l - 1) - target));
    }
    return ans;
  }
};

法2.暴力法(資料強度不是那麼的高)。對於每個以 i 位置結尾的,用 set 去重,可用滾動陣列的方式簡單優化下。

class Solution {
 public:
  int closestToTarget(vector<int>& arr, int target) {
    set<int>a[2];
    a[0].insert(INT_MAX);
    int ans(INT_MAX);
    for(int i = 0; i < arr.size(); ++i) {
      a[(i + 1) % 2].clear();
      for(int j : a[i % 2]) {
        a[(i + 1) % 2].insert(j & arr[i]);
        ans = min(ans, abs((j & arr[i]) - target));
      }
      a[(i + 1) % 2].insert(arr[i]);
      ans = min(ans, abs(arr[i] - target));
      a[i % 2].clear();
    }
    return ans;
  }
};