862. 和至少为 K 的最短子数组
先从朴素的思想去考:
枚举当前的前缀和
k
k
k,那么我们需要找到当前位置之前的满足差大于等于
k
k
k的最大位置。
i < j i<j i<j,且 p r e [ i ] ≥ p r e [ j ] pre[i]\geq pre[j] pre[i]≥pre[j],那么 p r e [ c u r ] − p r e [ i ] ≥ k pre[cur]-pre[i]\geq k pre[cur]−pre[i]≥k,必然有 p r e [ c u r ] − p r e [ j ] ≥ k pre[cur]-pre[j] \geq k pre[cur]−pre[j]≥k, i i i就没有存在的必要了。
就是说,如果一个人比你小(更靠近当前枚举的位置),还比你强(与当前前缀和作差得到更大的值),那你就没存在的必要了(doge)
维护一个递增的单调队列,每次从队头遍历所有 p r e [ c u r ] − p r e [ j ] ≥ k pre[cur]-pre[j]\geq k pre[cur]−pre[j]≥k的 j j j,并且同时弹出队头,因为与 p r e [ c u r ] − p r e [ j ] ≥ k pre[cur]-pre[j]\geq k pre[cur]−pre[j]≥k的 j j j,可以构成符合条件的最小子数组的右边界就是 c u r cur cur
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
int n = nums.size();
vector<long long> pre(n, -(1ll << 60));
vector<int> q(n);
int hh = 0, tt = -1;
int res = 0x3f3f3f3f;
for(int i = 0; i < n; ++i) {
pre[i] = nums[i];
if(i > 0) pre[i] += pre[i - 1];
if(pre[i] >= k) res = min(res, i + 1);
while(hh <= tt && pre[q[tt]] >= pre[i]) tt -= 1;
while(hh <= tt && pre[i] - pre[q[hh]] >= k) {
res = min(res, i - q[hh]);
hh += 1;
}
q[++tt] = i;
}
return res == 0x3f3f3f3f ? -1 : res;
}
};
30. 串联所有单词的子串
本题需要找到所有覆盖掉
w
o
r
d
s
words
words数组的
s
s
s中的子串
可以考虑枚举起点,因为
w
o
r
d
s
words
words数组中每个元素的长度一样,考虑枚举起点为
[
0
,
w
o
r
d
s
[
0
]
.
s
i
z
e
(
)
)
[0,words[0].size())
[0,words[0].size())。
这样可以有双指针去操作,当每次双指针判断后长度恰好为
w
o
r
d
s
[
0
]
.
s
i
z
e
(
)
∗
w
o
r
d
s
.
s
i
z
e
(
)
words[0].size()*words.size()
words[0].size()∗words.size(),那么就找到了这样一个子串,记录即可。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int len = words[0].size();
int cnt = words.size();
int total = cnt * len;
int n = s.size();
vector<int> ans;
unordered_map<string, int> act;
for(auto& u : words) act[u] += 1;
for(int i = 0; i < len; ++i) {
unordered_map<string, int> cur;
int l = i, r = i + len;
while(r <= n) {
string tword = s.substr(r - len, len);
if(act.count(tword)) {
int max_tword = act[tword];
auto& cur_tword = cur[tword];
if(cur_tword < max_tword) cur_tword += 1;
else {
while(cur_tword >= max_tword && l < r) {
cur[s.substr(l, len)] -= 1;
l += len;
}
cur_tword += 1;
}
} else {
cur.clear();
l = r;
}
if((r - l) / len == cnt) ans.push_back(l);
r += len;
}
}
return ans;
}
};
315. 计算右侧小于当前元素的个数
树状数组求逆序对或者归并求逆序对两种解法都可,下面是树状数组。
如果是用树状数组,可以采用对应的离散化操作进行即可。
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
int n = nums.size();
int base = 10001;
int maxv = base << 1 | 1;
vector<int> tr(maxv, 0);
vector<int> res(n, 0);
auto get = [&tr](int p) -> int {
int sum = 0;
while(p > 0) {
sum += tr[p];
p -= p & -p;
}
return sum;
};
auto add = [=, &tr](int p) {
while(p < maxv) {
tr[p] += 1;
p += p & -p;
}
};
for(int i = n - 1; i >= 0; --i) {
res[i] = get(nums[i] + base - 1);
add(nums[i] + base);
}
return res;
}
};
1201. 丑数III
标着是mid
,但是思维难度应该是hard
,但是实际做法难度也就是mid
难度,与丑数I 和丑数II并不同。
因为
n
≤
2
×
1
0
9
n\leq 2\times 10^9
n≤2×109,所以线性做法也不可以。
考虑二分答案,每次
c
h
e
c
k
check
check当前值之前是否有
n
n
n个即可。
class Solution {
public:
long long gcd(long long a, long long b) {
return b == 0 ? a : gcd(b, a % b);
}
int nthUglyNumber(int n, int _a, int _b, int _c) {
long long a = _a;
long long b = _b;
long long c = _c;
long long ab = a / gcd(a, b) * b;
long long bc = b / gcd(b, c) * c;
long long ac = a / gcd(a, c) * c;
long long abc = ab / gcd(ab, c) * c;
long long l = min({a, b, c}), r = 2e9;
while(l < r) {
long long mid = l + r >> 1;
if(mid / a + mid / b + mid / c - mid / ab - mid / ac - mid / bc + mid / abc >= n) r = mid;
else l = mid + 1;
}
return l;
}
};
815. 公交路线
#define sz(x) (int)x.size()
class Solution {
public:
int numBusesToDestination(vector<vector<int>>& r, int s, int t) {
if(s == t) return 0;
int n = r.size();
vector<vector<bool>> g(n, vector<bool>(n, false));
vector<int> dist(n, 0x3f3f3f3f);
for(auto &u : r) sort(u.begin(), u.end());
auto check = [](vector<int> &A, vector<int> &B) {
int i = 0, j = 0;
while(i < sz(A) && j < sz(B)) {
if(A[i] == B[j]) return true;
if(A[i] < B[j]) ++i; else ++j;
}
return false;
};
for(int i = 0; i < n; ++i)
for(int j = i + 1; j < n; ++j)
if(check(r[i], r[j])) g[i][j] = g[j][i] = 1;
queue<int> q;
for(int i = 0; i < n; ++i) {
auto it = lower_bound(r[i].begin(), r[i].end(), s);
if(it != r[i].end() && *it == s) q.push(i), dist[i] = 1;
}
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 0; i < n; ++i) {
int v = i;
if(g[u][v] && dist[v] > dist[u] + 1) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
int res = 0x3f3f3f3f;
for(int i = 0; i < n; ++i) {
auto it = lower_bound(r[i].begin(), r[i].end(), t);
if(it != r[i].end() && *it == t) res = min(res, dist[i]);
}
if(res == 0x3f3f3f3f) res = -1;
return res;
}
};