# Leetcode 第198场周赛题解

# Problem A - 换酒问题 (opens new window)

直接模拟即可。

参考代码(C++)
class Solution {
public:
    int numWaterBottles(int a, int b) {
        int ans = a, bottles = a;
        while (bottles >= b) {
            ans += bottles / b;
            bottles = bottles / b + bottles % b;
        }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11

# Problem B - 子树中标签相同的节点数 (opens new window)

DFS过程中记录当前节点子树中每种标签的数目即可。

参考代码(C++)
class Solution {
    int n;
    vector<int> ans;
    vector<vector<int>> adj;
    string labels;
    vector<int> dfs(int u, int p) {
        vector<int> cnt(26);
        cnt[labels[u] - 'a'] = 1;
        for (int v : adj[u]) {
            if (v != p) {
                vector<int> child = dfs(v, u);
                for (int i = 0; i < 26; ++i)
                    cnt[i] += child[i];
            }
        }
        ans[u] = cnt[labels[u] - 'a'];
        return cnt;
    }
public:
    vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
        ans = vector<int>(n);
        adj = vector<vector<int>>(n);
        this->labels = labels;
        this->n = n;
        for (auto v : edges) {
            adj[v[0]].emplace_back(v[1]);
            adj[v[1]].emplace_back(v[0]);
        }
        dfs(0, -1);
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# Problem C - 最多的不重叠子字符串 (opens new window)

首先提取出可以独立成立的所有区间,然后将这些区间按照左端点排序,再进行动态规划求取最优方案。

参考代码(C++)
class Solution {
public:
    vector<string> maxNumOfSubstrings(string s) {
        int n = s.size();
        vector<int> first(26, -1), last(26, -1);
        vector<vector<int>> v(26);
        for (int i = 0; i < n; ++i) {
            int d = s[i] - 'a';
            v[d].emplace_back(i);
            if (first[d] == -1)
                first[d] = i;
            last[d] = i;
        }
        for (int k = 0; k < 26; ++k) {
            for (int i = 0; i < 26; ++i) {
                if (first[i] == -1)
                    continue;
                    for (int j = 0; j < 26; ++j) {
                        if (j == i)
                            continue;
                        int l = first[i], r = last[i];
                        auto lit = lower_bound(v[j].begin(), v[j].end(), l);
                        auto rit = lower_bound(v[j].begin(), v[j].end(), r);
                        if (rit - lit > 0) {
                            first[i] = min(first[i], first[j]);
                            last[i] = max(last[i], last[j]);
                        }
                    }
                }
        }

        vector<pair<int, int>> seg;
        for (int i = 0; i < 26; ++i) {
            if (first[i] != -1)
                seg.emplace_back(first[i], last[i]);
        }
        sort(seg.begin(), seg.end());
        int num = 0, len = n;
        vector<int> best;
        int m = seg.size();
        vector<int> f(m), g(m);
        vector<vector<int>> h(m);
        for (int i = 0; i < m; ++i) {
            int clen = seg[i].second - seg[i].first + 1;
            f[i] = 1;
            g[i] = clen;
            h[i] = {i};
            for (int j = 0; j < i; ++j) {
                if (seg[j].second < seg[i].first) {
                    if (f[j] + 1 > f[i] || (f[j] + 1 == f[i] && g[j] + clen < g[i])) {
                        f[i] = f[j] + 1;
                        g[i] = g[j] + clen;
                        h[i] = h[j];
                        h[i].emplace_back(i);
                    }   
                }
            }
            if (f[i] > num || (f[i] == num && g[i] < len)) {
                num = f[i];
                len = g[i];
                best = h[i];
            }
        }
        vector<string> ans;
        for (int i : best)
            ans.emplace_back(s.substr(seg[i].first, seg[i].second - seg[i].first + 1));
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# Problem D - 找到最接近目标值的函数值 (opens new window)

容易发现,在ll固定时,func(arr,l,r)func(arr,l,r)的结果是单调递减的,因此可以再通过二分查找找到与targettarget最接近的两个值(一前一后),然后更新结果。为了快速计算func(arr,l,r)func(arr,l,r),可以预构建一个稀疏表。

参考代码(C++)
#define K 20

class Solution {
public:
    int closestToTarget(vector<int>& arr, int target) {
        int n = arr.size();
        vector<vector<int>> st(n, vector<int>(K));
        for (int i = 0; i < n; ++i)
            st[i][0] = arr[i];
        for (int k = 1; k < K; ++k) {
            for (int i = 0; i < n; ++i) {
                st[i][k] = st[i][k - 1];
                int r = i + (1 << (k - 1));
                if (r < n)
                    st[i][k] &= st[r][k - 1];
            }
        }
        auto query = [&](int l, int r) {
            if (l == r)
                return arr[l];
            int len = r - l + 1;
            int depth = log2(len - 1);
            return st[l][depth] & st[r - (1 << depth) + 1][depth];
        };
        int ans = INT_MAX;
        for (int l = 0; l < n; ++l) {
            int lo = l, hi = n - 1;
            while (lo <= hi) {
                int mid = (lo + hi) >> 1;
                int f = query(l, mid);
                if (f == target)
                    return 0;
                if (f < target)
                    hi = mid - 1;
                else
                    lo = mid + 1;
            }
            for (int i = max(l, lo - 1); i < min(n, lo + 1); ++i)
                ans = min(ans, abs(query(l, i) - target));
        }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

再观察一下,可以发现,在固定一个端点的情况下,无论另一端点如何取值,func(arr,l,r)func(arr,l,r)至多有logC\log C种不同的取值,因为只要某一位变为了00,之后就无法回到11。因此,我们可以得到一个更为简洁的解法。始终使用一个集合维护当前的合法取值,然后不断将右边的下一个数与集合中元素求与,并把该数也加入集合。从而,在任意时刻,这一集合都包含了右端点固定情况下的所有合法取值。因此,我们只需要遍历右端点,同时在每次遍历时计算当前集合中所有元素与目标值的曼哈顿距离,并更新答案。这样的总时间复杂度是O(NlogC)O(N\log C)

参考代码(Python3)
class Solution:
    def closestToTarget(self, arr: List[int], target: int) -> int:
        ans = abs(arr[0] - target)
        valid = {arr[0]}
        for num in arr:
            valid = {x & num for x in valid} | {num}
            ans = min(ans, min(abs(x - target) for x in valid))
        return ans
1
2
3
4
5
6
7
8