这是本文档旧的修订版!
数位DP
补题的时候顺带写了几道双指针水题…
1. LC 76:双指针
给两个串 S,T,求 S 的最短的包含 T 所有字母(需考虑个数)的子串。
双指针配合哈希计数统计即可:
class Solution: def minWindow(self, s: str, t: str) -> str: i, j, n, m = 0, 0, len(s), len(t) tot, ans = 0, (1e9, (0, -1)) chs = {c:t.count(c) for c in t} for j in range(n): if s[j] in chs: chs[s[j]] -= 1 if chs[s[j]] >= 0: tot += 1 while tot == m: if s[i] in chs: chs[s[i]] += 1 if chs[s[i]] > 0: tot -= 1 if ans[0] > j-i+1: ans = j-i+1, (i, j) i += 1 return s[ans[1][0]:ans[1][1]+1]
类似的还有:最长不重复子串、最长至多 k 个不同字符子串、寻找所有单词串联子串。
2. LOJ 2086:双指针+线段树
求所有至少k覆盖的区间组中,花费最少的区间组的花费(一组区间的花费定义为,最长区间的长度减去最短区间的长度)。
考虑将区间按长度排序,那么解肯定是连续取的一段。 注意如果 [i:j] 可,那么 [i:j+1]、[i-1:j] 一定都不是最优解。因此只需先枚举 i,然后 j 从 i 枚举到刚好至少 k 覆盖为止。维护一下数轴做区间+1和区间查最大就可以判断了。
复杂度 $O(n^2logn)$ 高了一点,考虑单调性,其实有很多重复操作(i 枚举了很长一段,到 i+1 又从头开始了,其实从 i 变成 i+1 的时候 j 应该至少从上次的 j 开始的),因此双指针同时枚举 ij 即可。复杂度 $O(nlogn)$:
#include <cstdio> #include <climits> #include <vector> #include <algorithm> #define IL inline using namespace std; constexpr int MN(1e6+7); template <typename vint, typename sint, typename xint = int> class STree { private: static constexpr xint ROOT = 1; struct Node { xint l, r; sint max, lza; } t[MN << 2]; xint ll, rr, pos; vint vv; #define li i<<1 #define ri i<<1|1 #define t_mid ((t[i].l+t[i].r) >> 1) #define add_v(i, v) ({t[i].max += v, t[i].lza += v;}) #define pd(i) \ ({ \ if (t[i].lza) \ { \ add_v(li, t[i].lza); \ add_v(ri, t[i].lza); \ t[i].lza = 0; \ } \ }) void build(const xint i, const xint l, const xint r) { t[i].l = l, t[i].r = r, t[i].lza = 0; if (l == r) t[i].max = 0; else { build(li, l, t_mid), build(ri, t_mid+1, r); t[i].max = max(t[li].max, t[ri].max);; } } void add(const xint i) { if (ll <= t[i].l && t[i].r <= rr) add_v(i, vv); else { pd(i); if (ll <= t_mid) add(li); if (rr > t_mid) add(ri); t[i].max = max(t[li].max, t[ri].max);; } } public: IL void build(const xint l, const xint r) { build(ROOT, l, r); } IL void add(const xint l, const xint r, const vint v) { ll = l, rr = r, vv = v, add(ROOT); } IL sint gmax() { return t[ROOT].max; } }; int tr[MN]; STree<int, int> st; struct Node { int l, r, len; IL bool operator <(const Node &o) const { return len < o.len; } } a[MN]; vector<int> vals; #define ID(x) lower_bound(vals.begin(), vals.end(), x)-vals.begin() int main() { int n, m; scanf("%d %d", &n, &m); for (int i=0; i<n; ++i) { scanf("%d %d", &a[i].l, &a[i].r); a[i].len = a[i].r-a[i].l; vals.emplace_back(a[i].l); vals.emplace_back(a[i].r); } sort(a, a+n); sort(vals.begin(), vals.end()); vals.erase(unique(vals.begin(), vals.end()), vals.end()); for (int i=0; i<n; ++i) a[i].l = ID(a[i].l), a[i].r = ID(a[i].r); int ans = INT_MAX; st.build(0, vals.size()-1); int l=0, r=0; while (r < n) { st.add(a[r].l, a[r].r, 1); while (st.gmax() >= m) { ans = min(ans, a[r].len - a[l].len); st.add(a[l].l, a[l].r, -1); ++l; } ++r; } if (ans == INT_MAX) puts("-1"); else printf("%d", ans); return 0; }
周末填坑