LeetCode-第 198 场周赛

第三题手速过慢,罚时爆炸,以至于第4题时间不足,赛后才写完ST预处理+二分答案。

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表(倍增解决RMQ问题的那个算法)预处理+二分答案

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 去重,可用滚动数组的方式简单优化下。(有题解是用 vector 然后 unique erase)
set 的大小最多就 \(20\) ,以 arr[i] 结尾,则每次变动一位,最多 \(20\) 位 (\(1e6 \approx 2^{20}\))。

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;
  }
};
posted @ 2020-07-20 09:27  菁芜  阅读(132)  评论(0编辑  收藏  举报