校招题
NC258932
其实就是一个三分答案的模板题,可以看出这是一个单谷函数。
借助这篇 博客 复习一下。
#include <bits/stdc++.h>
using namespace std;
using db = long double;
db eps = 1e-6;
void solve() {
db v0,x,y;
cin >> v0 >> x >> y;
auto f = [&] (db t) {
return t + y / (v0 + t * x);
};
db l = 0 , r = 1E12;
while (fabs(l - r) > eps) {
db md = (l + r) / 2;
db fl = f(md - eps) , fr = f(md + eps);
if (fl < fr) {
r = md;
} else {
l = md;
}
}
cout << fixed << setprecision(10) << f(l) << "\n";
}
int main() {
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
NC257435
抓住性质:一个数末尾 0 的个数 = 该数分解质因数后 2 的个数与 5 的个数的最小值。
- 10 = 2 × 5 → 1 个 0
- 100 = 2² × 5² → 2 个 0
- 20 = 2² × 5¹ → min(2,1) = 1 个 0
然后针对每个数为左端点,固定 \(2\) 合法范围(双指针),用桶统计 \(5\) 的个数。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int n,x;
cin >> n >> x;
vector<pair<int,int>> a(n);
for (int i = 0 ; i < n ; ++i) {
int num;
cin >> num;
while (num % 2 == 0) num /= 2 , a[i].first++;
while (num % 5 == 0) num /= 5 , a[i].second++;
}
sort(a.begin() , a.end());
LL ans = 0;
int r = n - 1;
vector<int> cnt5(31);
for (int i = 0 ; i < n ; ++i) {
if (i == r + 1) {
cnt5[a[i].second] -= 1;
r += 1;
}
while (r > i && a[i].first + a[r].first >= x) {
cnt5[a[r].second] += 1;
r--;
}
if (r + 1 >= n) continue;
int del = max(0 , x - a[i].second);
for (int j = del ; j <= 30 ; ++j) {
ans += cnt5[j];
}
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
NC260766
感觉像那种一步步优化的题目,挺有意思的
首先自然想到的是 \(dp\) 计数。
dp[i][j][k]表示到 \(i\) 位为止,末位两位是字符 \(j\),\(k\) 的字符串的价值总和。
dp1[i][j][k]表示到 \(i\) 位为止,末位两位是字符 \(j\),\(k\) 的字符串的个数。
自然有了下述代码的转移。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 1E9 + 7 , N = 1010;
LL dp[N][26][26],dp1[N][26][26];
void solve() {
LL n;
cin >> n;
if (n <= 2) {
cout << 0 << "\n";
return;
}
for (int i = 0 ; i < 26 ; ++i) {
for (int j = 0 ; j < 26 ; ++j) {
dp[2][i][j] = 0;
dp1[2][i][j] = 1;
}
}
for (int i = 3 ; i <= n ; ++i) {
for (int j = 0 ; j < 26 ; ++j) {
for (int k = 0 ; k < 26 ; ++k) {
for (int w = 0 ; w < 26 ; ++w) {
dp[i][k][w] = (dp[i][k][w] % mod + dp[i - 1][j][k] % mod + 1LL * (j == w) * dp1[i - 1][j][k] % mod) % mod;
(dp1[i][k][w] += dp1[i - 1][j][k]) %= mod;
}
}
}
}
int ans = 0;
for (int j = 0 ; j < 26 ; ++j) {
for (int w = 0 ; w < 26 ; ++w) {
(ans += dp[n][j][w]) %= mod;
// cout << dp[n][j][w] << " ";
}
// cout << "\n";
// cout << "\n";
}
cout << ans << "\n";
// cout << (27 * 26 + 1) * 26 << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
\(dp\) 和 \(dp1\) 肯定都有高度的对称性 (观察转移方程)
打表 \(3\),\(4\),\(5\)
\(dp[n][i][j]\) 分别为 \(1\) , \(52\) , \(2028\)
\(dp1[n][i][j]\) 分别为 \(26\) , \(676\) , \(17576\)
观察得到:1 * 26 + 26 = 52 , 52 * 26 + 676 = 2028
由于 \(n\) 为 \(1E12\) , 启示我们用矩阵快速幂解决这个问题。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 1E9 + 7;
struct Mat {
int a[2][2];
};
Mat operator * (Mat l, Mat r) {
Mat t;
t.a[0][0] = t.a[0][1] = t.a[1][0] = t.a[1][1] = 0;
for (int i = 0 ; i < 2 ; ++i) {
for (int j = 0 ; j < 2 ; ++j) {
for (int k = 0 ; k < 2 ; ++k) {
t.a[i][j] = (t.a[i][j] % mod + 1LL * l.a[i][k] * r.a[k][j] % mod) % mod;
}
}
}
return t;
}
void solve() {
LL n;
cin >> n;
if (n <= 2) {
cout << 0 << "\n";
return;
}
/*
[1 , 26] , [26 , 0] --> [27 , 26] -> []
[0 , 0] , [1 , 26] [0 , 0]
*/
Mat l,r,base;
l.a[0][0] = 1 , l.a[0][1] = 26, l.a[1][0] = 0 , l.a[1][1] = 0;
r.a[0][0] = 26 , r.a[0][1] = 0 , r.a[1][0] = 1 , r.a[1][1] = 26;
base.a[0][0] = 1 , base.a[0][1] = 0 , base.a[1][0] = 0 , base.a[1][1] = 1;
n -= 3;
while (n) {
if (n & 1) base = base * r;
r = r * r;
n >>= 1;
}
l = l * base;
cout << 1LL * l.a[0][0] * 26 % mod * 26 % mod << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
NC261663
首先 \(k\) 个数肯定又要用上,贪心地想,肯定是排序后的前后缀。
经典做法,长度固定,枚举前缀/后缀即可。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using db = long double;
void solve() {
int n,k;
cin >> n >> k;
vector<db> a(n);
for (int i = 0 ; i < n ; ++i) {
cin >> a[i];
}
sort(a.begin() , a.end());
if (k == 1) {
cout << *max_element(a.begin() , a.end()) - *min_element(a.begin() , a.end()) << "\n";
return;
}
if (k == n) {
cout << 0 << "\n";
return;
}
vector<db> pre(n);
for (int i = 0 ; i < n ; ++i) {
pre[i] = a[i] + (i == 0 ? 0 : pre[i - 1]);
}
auto get_sum = [&] (int l,int r) {
if (l > r) return (db)0;
return pre[r] - (l == 0 ? 0 : pre[l - 1]);
};
db ans = 1E18;
for (int i = 0 ; i < n && n - (k - i) >= i && i <= k; ++i) {
db l = get_sum(0 , i - 1) , r = get_sum(n - (k - i) , n - 1);
db avg = (l + r) / k;
db mn = min({avg , a[i] , a[n - (k - i) - 1] }) , mx = max({avg , a[i] , a[n - (k - i) - 1] });
ans = min(ans , mx - mn);
}
cout << fixed << setprecision(10) << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
NC256071
贪心,路程一定要短,那么菊花图最合适。
把\(2\),\(3\)数量较多的那个作为根即可,剩下的就是计数。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 1E9 + 7;
void solve() {
int n;
vector<int> cnt(2);
cin >> n >> cnt[0];
cnt[1] = n - cnt[0];
int rt = (cnt[0] > cnt[1]) ? 0 : 1;
auto qsm = [&] (int a,LL b,int mod) {
int ans = 1;
while (b) {
if (b & 1) ans = 1LL * ans * a % mod;
a = 1LL * a * a % mod;
b >>= 1;
}
return ans;
};
auto mul = [&] (int x,int y) {
int z = 1LL * x * y % mod;
return z;
};
int ans = 1;
//菊花图
//rt
ans = mul(ans , qsm(3 , 1LL * (cnt[rt] - 1) , mod)); // rt ^ 2;
ans = mul(ans , qsm(4 , 1LL * cnt[rt ^ 1] , mod)); //6
//rt类
LL num = 1LL * (cnt[rt] - 1) * (cnt[rt] - 2) / 2;
ans = mul(ans , qsm(4 , num , mod)); //rt ^ 3
//(rt ^ 1) 类
num = 1LL * cnt[rt ^ 1] * (cnt[rt ^ 1] - 1) / 2;
ans = mul(ans , qsm(6 , num , mod)); //rt * (rt ^ 1) * (rt ^ 1); //6
num = 1LL * (cnt[rt] - 1) * cnt[rt ^ 1];
ans = mul(ans , qsm(6 , num , mod)); //rt * rt * (rt ^ 1)
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
NC258420
众数的数量不是 \(n\) 就是 \(n - 1\)。
\(n\) 的情况就是总和是 \(n\) 的倍数,\(n - 1\) 就是不是倍数,拿出一个数出来调节。
怎么算代价呢?大于和小于\(avg\)两边调整代价的最大值。
\(n - 1\) 的话,把最大/小作为调节的数。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int n;
cin >> n;
LL sum = 0;
vector<int> a(n);
for (int i = 0 ; i < n ; ++i) {
cin >> a[i];
sum += a[i];
}
sort(a.begin() , a.end());
auto cal = [&] (int avg,int idx) {
LL ans = 0;
for (int i = 0 ; i < n ; ++i) {
if (i == idx) continue;
if (a[i] > avg) ans += a[i] - avg;
}
return ans;
};
if (sum % n == 0) {
cout << cal(sum / n , -1) << "\n";
return;
}
LL ans = 1E18;
//sum - a[0] = (n - 1) * avg + k;
//分配一下有 (n - 1 - k) 个 avg , k 个 avg + 1;
//如果全部变成 avg 的话, 算大于 avg 贡献即可, 多出来的算到 a[0]
//如果全部变成 avg + 1 的话,其实算小于 avg 的贡献,但如果算大于 avg 贡献的话,则要补偿 n - 1 - k
//其实两边变化取 max
int avg = (sum - a[0]) / (n - 1) , k = (sum - a[0]) % (n - 1);
ans = min({ans , cal(avg , 0) , cal(avg + 1 , 0) + n - 1 - k});
avg = (sum - a[n - 1]) / (n - 1) , k = (sum - a[n - 1]) % (n - 1);
ans = min({ans , cal(avg , n - 1) , cal(avg + 1 , n - 1) + n - 1 - k});
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
华为机试编程模拟题4 第三题
其实很简单,但我思考惯性了,把 \(k\) 加入的 \(dp\) 维度,导致空间炸了。
其实就是常规的二维 \(max \ dp\) , 但我们只取最后一行,离中间点距离在 \(k\) 之内的点。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 305;
void solve() {
int n,K;
cin >> n >> K;
vector<vector<int>> a(n);
for (int i = 0 ; i < n ; ++i) {
a[i].resize(2 * (i + 1) - 1);
for (int j = 0 ; j < 2 * (i + 1) - 1 ; ++j) {
cin >> a[i][j];
}
}
const LL INF = 1E18;
vector<vector<LL>> dp(n);
for (int i = 0 ; i < n ; ++i) {
dp[i].resize(2 * (i + 1) - 1);
for (int j = 0 ; j < 2 * (i + 1) - 1 ; ++j) {
dp[i][j] = -INF;
}
}
auto check = [&] (int i,int j) {
return (0 <= j && j < 2 * (i + 1) - 1);
};
dp[0][0] = a[0][0];
for (int i = 1 ; i < n ; ++i) {
for (int j = 0 ; j < 2 * (i + 1) - 1 ; ++j) {
if (check(i - 1 , j)) {
dp[i][j] = max(dp[i][j], dp[i - 1][j] + a[i][j]);
}
if (j - 1 >= 0 && check(i - 1 , j - 1)) {
dp[i][j]= max(dp[i][j], dp[i - 1][j - 1] + a[i][j]);
}
if (j - 2 >= 0 && check(i - 1 , j - 2)) {
dp[i][j] = max(dp[i][j] , dp[i - 1][j - 2] + a[i][j]);
}
}
}
LL ans = -INF;
//n - 1 , n - 1 - k , n - 1 + k
for (int j = max(0 , n - 1 - K) ; j <= min(2 * n - 2 , n - 1 + K) ; ++j) {
ans = max(ans , dp[n - 1][j]);
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
华为机试模拟 8
补充:
-
将数字加入字符串
s += (1 + '0'); -
stl查询t是否是s的子串s.find(t) != string::npos; //t是s的子串 -
求最长不下降子序列
o(nlogn)(非数据结构)
相较于传统的想法维护 dp[i] 以 i 结尾的最长满足序列,我们反过来维护长度序列 d。
d[x] 代表在 dp 更新中,长度为 x 的序列中,末尾最小值为 d[x]。
d[x] 肯定越小越好,仅需找出最后一个大于等于 a[i] 的 d[x] , 更新 d[x + 1] 为 a[i] 即可,发现这样恰恰能维护 d[x] 非降序。
#include <bits/stdc++.h>
using LL = long long;
using namespace std;
void solve() {
int n;
cin >> n;
vector<pair<int,int>> car(n);
for (int i = 0 ; i < n ; ++i) {
cin >> car[i].first >> car[i].second;
}
sort(car.begin() , car.end());
//最长不下降子序列
const int inf = 2E9;
vector<int> dp(n + 1 , inf);
dp[0] = -inf;
int ans = 0;
for (int i = 0 ; i < n ; ++i) {
auto it = upper_bound(dp.begin() , dp.end() , car[i].second);
if (it == dp.begin()) {
continue;
}
int x = prev(it) - dp.begin();
ans = max(ans , x + 1);
dp[x + 1] = min(dp[x + 1] , car[i].second);
}
cout << n - ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}
力扣 寻找两个正序数组的中位数
简单描述:给定两个有序数组 (长度分别为 m 和 n),如何在 O(log(n + m)) 的时间复杂度内,找到两个数组的中位数。
-
设总长度为
total = n + m, 如果total为奇数的话,则是total / 2的位置,否则就是total / 2和total / 2 + 1的平均值。 -
其实抽象一下,如何在
log(k)的复杂度内,找到合并后数组的第k位呢? -
对比
a[k / 2 - 1]和b[k / 2 - 1]的大小 (坐标从0开始)。- 如果
a[k / 2 - 1] < b[k / 2 - 1], 则a[k / 2 - 1]最多都只有k / 2 - 2比它小 , 那么从a[0]到a[k / 2 - 1]都可以舍去,k也已经减去k / 2个数。 - 反之亦然,并且
=的情况放在>=或者<=均可。 - 边界问题直接看代码即可。
- 如果
class Solution {
public:
int find_k(vector<int>& a,vector<int>& b,int k) {
int n = a.size() , m = b.size();
int idx_a = 0 , idx_b = 0;
while (true) {
if (idx_a == n) return b[idx_b + k - 1];
if (idx_b == m) return a[idx_a + k - 1];
if (k == 1) return min(a[idx_a] , b[idx_b]);
int now_idx_a = min(n - 1 , idx_a + k / 2 - 1);
int now_idx_b = min(m - 1 , idx_b + k / 2 - 1);
if (a[now_idx_a] < b[now_idx_b]) {
k -= now_idx_a - idx_a + 1;
idx_a = now_idx_a + 1;
} else {
k -= now_idx_b - idx_b + 1;
idx_b = now_idx_b + 1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size() , m = nums2.size();
int total = n + m;
if (total % 2) {
return 1.0 * find_k(nums1 , nums2 , total / 2 + 1);
} else {
return (find_k(nums1 , nums2 , total / 2) + find_k(nums1 , nums2 , total / 2 + 1)) / 2.0;
}
}
};
接雨水
对于位置 \(i\) 能接的雨水等于左右边最大高度的最小值减去当前高度。
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
vector<int> l(n) , r(n);
for (int i = 0 ; i < n ; ++i) {
l[i] = i == 0 ? height[i] : max(height[i] , l[i - 1]);
}
for (int i = n - 1 ; i >= 0 ; --i) {
r[i] = i == n - 1 ? height[i] : max(height[i] , r[i + 1]);
}
int ans = 0;
for (int i = 0 ; i < n ; ++i) {
ans += min(l[i] , r[i]) - height[i];
}
return ans;
}
};
NC256063
双指针维护,注意边界产生的贡献即可。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
LL n;
int m1,m2;
cin >> n >> m1 >> m2;
vector<pair<int,LL>> line1(m1),line2(m2);
for (int i = 0 ; i < m1 ; ++i) {
cin >> line1[i].first >> line1[i].second;
}
for (int i = 0 ; i < m2 ; ++i) {
cin >> line2[i].first >> line2[i].second;
}
int idx1 = 0 , idx2 = 0;
LL l1 = 0 , l2 = 0 , ans = 0;
while (idx1 < m1 && idx2 < m2) {
LL r1 = l1 + line1[idx1].second - 1;
LL r2 = l2 + line2[idx2].second - 1;
LL l = max(l1 , l2) , r = min(r1 , r2);
//算边界的贡献
if (line1[idx1].first == line2[idx2].first) {
if (l1 < l2 || l2 < l1) {
++ans;
} else if (l1 == l2 && idx1 > 0 && idx2 > 0 && line1[idx1 - 1] == line2[idx2 - 1]) {
++ans;
}
} else {
//交接贡献
if (l <= r) {
ans += r - l;
}
//算边界贡献
if (l1 < l && idx2 > 0 && line2[idx2 - 1].first == line1[idx1].first) {
++ans;
}
if (l2 < l && idx1 > 0 && line1[idx1 - 1].first == line2[idx2].first) {
++ans;
}
if (l1 == l2 && idx1 > 0 && idx2 > 0 && line1[idx1 - 1].first == line2[idx2].first
&& line2[idx2 - 1].first == line1[idx1].first) {
++ans;
}
}
if (r1 < r2) {
l1 = r1 + 1;
idx1 += 1;
} else if (r2 < r1) {
l2 = r2 + 1;
idx2 += 1;
} else {
l1 = r1 + 1 , l2 = r2 + 1;
idx1 += 1 , idx2 += 1;
}
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
}
动态中位数
对顶堆维护
class MedianFinder {
public:
priority_queue<int> Min;
priority_queue<int , vector<int> , greater<int>> Max;
MedianFinder() {}
void addNum(int num) {
if (Min.empty() || num <= Min.top()) {
Min.push(num);
if (Max.size() + 1 < Min.size()) {
Max.push(Min.top());
Min.pop();
}
} else {
Max.push(num);
if (Max.size() > Min.size()) {
Min.push(Max.top());
Max.pop();
}
}
}
double findMedian() {
if (Min.size() > Max.size()) {
return Min.top();
} else {
return (Min.top() + Max.top()) / 2.0;
}
}
};
力扣 最长有效括号

-
思路一
设
dp[i]为以i为结尾的最长合法序列,显然只有)结尾的有效。如果
s[i - 1] == '(', 那么dp[i] = dp[i - 2] + 2如果
s[i - 1] == '), 那么dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2(仅在s[i - dp[i - 1] - 1] == '('有效)。class Solution { public: int longestValidParentheses(string s) { int n = s.size() , ans = 0; vector<int> dp(n , 0); for (int i = 1 ; i < n ; ++i) { if (s[i] == ')') { if (s[i - 1] == '(') dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; else { if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] == '(') { dp[i] = dp[i - 1] + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0) + 2; } } } ans = max(ans , dp[i]); } return ans; } }; -
思路二

class Solution { public: int longestValidParentheses(string s) { int n = s.size() , ans = 0; vector<int> stk; stk.push_back(-1); for (int i = 0 ; i < n ; ++i) { if (s[i] == '(') { stk.push_back(i); } else { stk.pop_back(); if (stk.empty()) { stk.push_back(i); } else { ans = max(ans , i - stk.back()); } } } return ans; } }; -
思路三

class Solution { public: int longestValidParentheses(string s) { int ans = 0 , n = s.size(); int left = 0 , right = 0; for (int i = 0 ; i < n ; ++i) { if (s[i] == '(') { ++left; } else { ++right; } if (right > left) { left = right = 0; } else if (left == right) { ans = max(ans , left * 2); } } left = right = 0; for (int i = n - 1 ; i >= 0 ; --i) { if (s[i] == '(') { ++left; } else { ++right; } if (left > right) { left = right = 0; } else if (left == right) { ans = max(ans , left * 2); } } return ans; } };
力扣 最短回文串
题目的本质就是找最长回文前缀
- 思路一
直接利用 Manacher
class Solution {
public:
string get(string t) {
string s = "#";
for (auto x : t) {
s += x;
s += "#";
}
return s;
}
vector<int> Manacher(string s) {
int n = s.size();
vector<int> f(n);
for (int i = 0 , j = 0 ; i < n ; i++) {
if (2 * j - i >= 0 && j + f[j] > i) {
f[i] = min(f[2 * j - i] , j + f[j] - i);
}
while (i - f[i] >= 0 && i + f[i] < n && s[i - f[i]] == s[i + f[i]]) {
f[i] += 1;
}
if (i + f[i] > j + f[j]) {
j = i;
}
}
return f;
}
string shortestPalindrome(string s) {
int m = s.size();
string t = get(s);
auto f = Manacher(t);
int n = t.size();
int id = 0;
for (int i = 0 ; i < n ; ++i) {
if (i - f[i] + 1 == 0) {
id = i;
}
}
string ans = "";
for (int i = n - 1 ; i > id + f[id] - 1 ; --i) {
if (t[i] != '#') ans += t[i];
}
for (int i = 0 ; i < n ; ++i) {
if (t[i] != '#') ans += t[i];
}
return ans;
}
};
-
思路二
字符串
hash, 一个正着,一个反着,看相不相同。class Solution { public: using uLL = unsigned long long; string shortestPalindrome(string s) { int n = s.size() , id = 0; uLL base = 9999991 , mul = 1 , pre = 0 , suf = 0; for (int i = 0 ; i < n ; ++i) { pre = pre * base + (s[i] - 'a' + 1); suf = suf + (s[i] - 'a' + 1) * mul; mul = mul * base; if (pre == suf) { id = i; } } string ans = ""; for (int i = n - 1 ; i > id ; --i) { ans += s[i]; } return ans + s; } }; -
思路三
-
看到前缀,立马想到
kmp中border的定义。 -
用正序匹配逆序,如果能匹配上,就是回文相等。
class Solution { public: vector<int> kmp(string s) { int n = s.size(); vector<int> f(n + 1); for (int i = 1 , j = 0; i < n ; ++i) { while (j && s[i] != s[j]) { j = f[j]; } j += (s[i] == s[j]); f[i + 1] = j; } return f; } string shortestPalindrome(string s) { string t = s; reverse(t.begin() , t.end()); auto f = kmp(s); int n = s.size() , id = 0; for (int i = 0 , j = 0 ; i < n ; ++i) { while (j && (j == n || s[j] != t[i])) { j = f[j]; } j += (t[i] == s[j]); id = j; } // cout << id ; string tmp = ""; for (int i = n - 1 ; i >= id ; --i) { tmp += s[i]; } return tmp + s; } };- 注意遍历完
t时的j值,因为要保证前缀回文中的 “前缀” 二字。
-
力扣 确实的第一个正数
要求时间复杂度 o(n),空间为常数
-
思路一
关键在于要利用
nums数组本身- 把
<= 0的数全赋值为n + 1 - 遍历数组,把
abs(nums[i]) - 1位置的数取反 - 再次遍历,找到第一个
>= 0的位置。
class Solution { public: int firstMissingPositive(vector<int>& nums) { int n = nums.size(); for (int i = 0 ; i < n ; ++i) { if (nums[i] <= 0) nums[i] = n + 1; } for (int i = 0 ; i < n ; ++i) { int val = abs(nums[i]); if (1 <= val && val <= n) nums[val - 1] = -abs(nums[val - 1]); } for (int i = 0 ; i < n ; ++i) { if (nums[i] >= 0) return i + 1; } return n + 1; } }; - 把
-
思路二
类似于环形换位置
class Solution { public: int firstMissingPositive(vector<int>& nums) { int n = nums.size(); for (int i = 0 ; i < n ; ++i) { while (1 <= nums[i] && nums[i] <= n && nums[nums[i] - 1] != nums[i]) { swap(nums[nums[i] - 1] , nums[i]); } } for (int i = 0 ; i < n ; ++i) { if (nums[i] != i + 1) { return i + 1; } } return n + 1; } };
力扣 计算右侧小于当前元素的个数
-
思路一
离散化树状数组
class Solution { public: vector<int> countSmaller(vector<int>& nums) { int n = nums.size(); vector<int> P; for (int i = 0 ; i < n ; ++i) { P.push_back(nums[i]); } sort(P.begin() , P.end()); P.erase(unique(P.begin() , P.end()) , P.end()); auto find = [&] (int x) { return lower_bound(P.begin() , P.end() , x) - P.begin() + 1; }; auto lowbit = [&] (int x) { return x & (-x); }; int m = P.size(); vector<int> tr(m + 1); auto add = [&] (int x) { while (x <= m) { tr[x] += 1; x += lowbit(x); } }; auto getsum = [&] (int x) { int ans = 0; while (x) { ans += tr[x]; x -= lowbit(x); } return ans; }; vector<int> ans(n); for (int i = n - 1 ; i >= 0 ; --i) { int pos = find(nums[i]); if (pos != 1) ans[i] = getsum(pos - 1); add(pos); } return ans; } }; -
思路二
归并排序
class Solution { public: vector<int> countSmaller(vector<int>& nums) { int n = nums.size(); vector<pair<int,int>> nod(n); for (int i = 0 ; i < n ; ++i) { nod[i] = make_pair(nums[i] , i); } vector<int> ans(n); function<void(int,int)> mergesort =[&] (int l,int r) { if (l == r) return; int md = (l + r) / 2; mergesort(l , md) , mergesort(md + 1 , r); int i = l , j = md + 1 , pos = 0; vector<pair<int,int>> tmp(r - l + 1); while (i <= md && j <= r) { if (nod[j].first < nod[i].first) tmp[pos ++ ] = nod[j ++ ]; else { ans[nod[i].second] += (j - md - 1); tmp[pos ++] = nod[i ++]; } } while (i <= md) { ans[nod[i].second] += (j - md - 1); tmp[pos ++ ] = nod[i ++]; } while (j <= r) { tmp[pos ++] = nod[j ++]; } for (int k = l; k <= r ; ++k) { nod[k] = tmp[k - l]; } }; mergesort(0 , n - 1); return ans; } };
力扣 网格图中最少访问的格子数
-
思路一
其实最显然的想法是维护 \([i + 1 , i + grid[i][j]]\) 和 \([j + 1 , j + grid[i][j]]\) 区间问题,这个东西树状数组和线段树都能做到。
-
思路二
把 \((i,j)\) 坐标拆开,考虑到往下走和往右走两者情况,维护每一行和每一列的优先队列。
-
思路三
看到转移的代价为 \(1\) , 其实队列就行了。
但不可以在区间数一个个去试到底转移了没有,不然就\(o(n^3)\)。
由转移代价为 \(1\) 的贪心性质,如果转移过了,就不用再转移了。
基于此,那么对于每一行或者每一列都维护一个并查集,直接加速就可以了。
对于 \(grid[i][j]\),它又在 \(i\) 行上,又在 \(j\) 列上,那么只用看先用行遍历到它,还是先用列遍历到它,用个 \(used[i][j]\) 维护就好了。
时间复杂度 \(O(n * m * log(n * m))\)
class Solution { public: const int inf = 1E9; int minimumVisitedCells(vector<vector<int>>& grid) { int n = grid.size() , m = grid[0].size(); vector<vector<int>> row(n , vector<int>(m + 1)) , col(m , vector<int>(n + 1)); for (int i = 0 ; i < n ; ++i) { for (int j = 0 ; j <= m ; ++j) { row[i][j] = j; } } for (int j = 0 ; j < m ; ++j) { for (int i = 0 ; i <= n ; ++i) { col[j][i] = i; } } function<int(int , int , int)> find = [&] (int x,int c,int ty) { if (ty == 0) { return col[c][x] == x ? x : col[c][x] = find(col[c][x] , c , 0); } else { return row[c][x] == x ? x : row[c][x] = find(row[c][x] , c , 1); } }; vector<vector<int>> used(n , vector<int>(m)); vector<vector<int>> dp(n , vector<int>(m , inf)); dp[0][0] = 1; used[0][0] = 1; queue<pair<int,int>> Q; Q.push(make_pair(0 , 0)); while (!Q.empty()) { auto [i , j] = Q.front(); Q.pop(); int ri = min(i + grid[i][j] , n - 1) , rj = min(j + grid[i][j] , m - 1); int li = find(i + 1 , j , 0); while (li <= ri) { if (!used[li][j]) { used[li][j] = 1; dp[li][j] = dp[i][j] + 1; Q.push(make_pair(li , j)); } if (li + 1 <= n) col[j][li] = li + 1; li = find(li + 1 , j , 0); } int lj = find(j + 1 , i , 1); while (lj <= rj) { if(!used[i][lj]) { used[i][lj] = 1; dp[i][lj] = dp[i][j] + 1; Q.push(make_pair(i , lj)); } if (lj + 1 <= m) row[i][lj] = lj + 1; lj = find(lj + 1 , i , 1); } } if (dp[n - 1][m - 1] == inf) dp[n - 1][m - 1] = -1; return dp[n - 1][m - 1]; } };

浙公网安备 33010602011771号