LeetCode 力扣周赛 239

94 阅读3分钟

1848. 到目标元素的最小距离

时间复杂度O(n)

start 向两端寻找 target,找到的第一个目标元素即最近的。

class Solution {
public:
    int getMinDistance(vector<int>& nums, int target, int start) {
        for (int i = 0, n = nums.size(); i < n; i++) {
            if ((start+i < n && nums[start+i] == target) || (start-i >= 0 && nums[start-i] == target)) {
                return i;
            }
        }
        return 0;
    }
};

1849. 将字符串拆分为递减的连续值

知识点递归

时间复杂度O(n^2)

因为两个数字相差必须是1,所以只要前一个数字确定了,当前数字的值就确定了。

首先,枚举第一个数字的长度,一旦长度确定了,值就确定了,后续数字的值也就确定了。然后按值尝试分割字符串即可。

需要注意的是,如果直接把s转换为数字,有可能超出int64的取值范围。但是因为要将s分割成两个数字,所以第一个数字的上限是999,999,999,9

class Solution {
public:
    int64_t toNumber(const std::string &s, int l, int r) {
        int64_t anw = 0;
        while(l <= r) {
            anw *= 10;
            anw += (s[l] - '0');
            l++;
        }
        return anw;
    }
    bool check(const std::string &s, int pos, int64_t pre) {
        if (pos == s.size()) {
            return true;
        }
        for (int i = pos; i < s.size(); i++) {
            int64_t val = toNumber(s, pos, i);
            if (val == pre-1) {
                if (check(s, i+1, val)) {
                    return true;
                }
            } else if (val >= pre) {
                return false;
            }
        }
        return false;
    }
    bool splitString(string s) {
        if (s.size() <= 1) {
            return false;
        }
        int n = s.size()-1;
        int64_t limit = 1L<<40;
        for (int i = 0; i < n; i++) {
            int64_t val = toNumber(s, 0, i);
            if (val > limit) {
                return false;
            }
            if (check(s, i+1, val)) {
                return true;
            }
        }
        return false;
    }
};

1850. 邻位交换的最小次数

知识点next_permutation

时间复杂度O(n^2)

设第 k 个最小妙数为target,如果能构造出 target,则求最小次数是很简单的。那问题变成了如何构造 target。

竞赛时可以借助库函数 next_permutation 构造target。不过面试时还是要能手写next_permutation。接下来简单介绍下next_permutation的实现。

设有字符串s,及s的下一个排列 p,两者长度为n,最长公共前缀的长度为 c。则必有:

  • 构成 p[c .. n)s[c .. n) 的字符集合相同。
  • p[c]s[c+1 .. n) 中,大于s[c]的最小字符。
  • p[c+1 .. n) 升序排列。

那么将 s 构造为 p,只需要三步:

  • 找到s中满足s[i] < s[i+1] 的最大的i,该 i 即为 c
  • 找到 s[c+1 .. n) 中大于s[c]的最小字符s[t],并将两者交换。
  • s[c+1 .. n] 升序排列。

现在我们可以重复k次上述构造过程,得到target,然后模拟题目的交换过程并计数,即可得到答案。

class Solution {
public:
    int getMinSwaps(string num, int k) {
        cout << s << endl;
        string old = num;
        int end = num.size()-1;
        while(k--) {
            for (int i = end; i >= 1; i--) {
                if (num[i-1] < num[i]) {
                    int l = i-1;
                    while(i+1 <= end && num[l] < num[i+1]) {
                        i++;
                    }
                    swap(num[l], num[i]);
                    reverse(num.begin() + l + 1, num.end());
                    break;
                }
            }
        }
        int anw = 0;
        for (int i = end; i >= 0; i--) {
            if (num[i] != old[i]) {
                int j = i-1;
                while(old[j] != num[i]) {
                    j--;
                }
                anw += i-j;
                while(j < i) {
                    swap(old[j], old[j+1]);
                    j++;
                }
            }
        }
        return anw;
    }
};

1851. 包含每个查询的最小区间

知识点离线处理

时间复杂度O(n*lgn)

设有 left, right 分别存储了将interval按左右端点排序之后的结果。

设当前正在被处理的询问为 query

lp 为满足 left[lp][0] 不超过 query 的最大坐标,即left[0 .. lp] 中所有区间的左端点都在query左边或与query相等,而其他区间的左端点都在query右边。

rp 为满足 right[rp][1] 小于 query 的最大坐标,及right[0 .. rp] 中的所有区间的右端点都在query的左边,而其他区间的右端点都在query的右边或与query相等。

换言之,在left[0 .. lp]中而不在right[0 .. rp]中的最短的区间的长度,即为query的答案。

在代码实现上,可以用一个 multiset 来维护这部分数据:

  • 首先对于当前的query,先找到lp,将left[0 .. lp]中所有区间的长度都插入到容器中。
  • 然后找到rp,将right[0 .. rp]中所有区间的长度都从容器中删除。
  • 容器中最小的元素即为答案,如果容器为空,则答案不存在。

另外,如果所有的查询 queries 是有序的,不难发现,随着query的增大,lprp是单调递增的,也就是说,没必要每次都从0开始寻找lprp并维护容器。而是可以从前一次查询的基础上,继续维护容器内的元素。

class Solution {
public:
    vector<int> minInterval(vector<vector<int>>& l, vector<int>& queries) {
        multiset<int> len;
        auto r = l;
        sort(l.begin(), l.end(), [](const auto &lhs, const auto &rhs) { return lhs[0] < rhs[0]; });
        sort(r.begin(), r.end(), [](const auto &lhs, const auto &rhs) { return lhs[1] < rhs[1]; });

        vector<vector<int>> query;
        for (int i = 0; i < queries.size(); i++) {
            query.push_back(std::vector<int>{queries[i], i});
        }
        sort(query.begin(), query.end(), [](const auto &lhs, const auto &rhs) { return lhs[0] < rhs[0];});
        int lp = 0, rp = 0;
        vector<int> anw(query.size(), 0);
        for (auto q : query) {
            while (lp < l.size() && l[lp][0] <= q[0]) {
                len.insert(l[lp][1] - l[lp][0] + 1);
                lp++;
            }
            while (rp < r.size() && r[rp][1] < q[0]) {
                auto it = len.find(r[rp][1] - r[rp][0] + 1);
                len.erase(it);
                rp++;
            }
            anw[q[1]] = len.empty() ? -1 : *len.begin();
        }
        return anw;
    }
};