🔰🔰🔰:困難
🔰🔰:中等
🔰:簡單
文章目錄
- `🔰🔰🔰`927.三等分
- `🔰🔰`811.子域名通路計數
- `🔰🔰`921.使括号有效的最少添加
- `🔰🔰`287.尋找重複數
- 法一: 原地哈希
- `🔰🔰`(貪心)870.優勢洗牌
- `🔰🔰`856.括号的分數
- `🔰🔰🔰`(狀壓DP)801.使序列遞增的最小交換次數
- `🔰🔰`53.最大子數組和
- `🔰`1790.僅執行一次字元串交換能否使兩個字元串相等
- `🔰🔰`131.分割回文串
- 遞歸回溯
- 通過dp預處理優化
- `🔰🔰`5.最長回文子串
- `🔰🔰`817.連結清單元件
- `🔰🔰`769.最多能完成排序的塊
- `🔰🔰🔰`940.不同的子序列 II
- 初步優化
- `🔰🔰`1441.用棧操作建構數組
- `🔰🔰🔰`6207.統計定界子數組的數目
- `🔰🔰`886.可能的二分法
- dfs
- 并查集
- `🔰🔰`904.水果成籃
- 優化
- `🔰🔰🔰`902.最大為 N 的數字組合
- dfs, 逾時
- 數位DP
- `🔰`1700.無法吃午餐的學生數量
- `🔰🔰`779.第K個文法符号
- 遞歸模拟
- ✨位運算, 奇偶校驗
- (單調棧)`🔰🔰`901.股票價格跨度
- `🔰🔰🔰`1235.規劃兼職工作
- 動态規劃
- 二分優化
- `🔰`1768.交替合并字元串
- `🔰🔰`915.分割數組
- bfs+dfs `🔰🔰`934.最短的橋
- `🔰🔰`560.和為 K 的子數組
- `🔰🔰🔰`862.和至少為 K 的最短子數組
- 暴力解法, 逾時
- 雙端隊列
- (單調棧)`🔰🔰`907.子數組的最小值之和
- 🤙歡迎關注泥煙的客棧(常在這裡更新)
🔰🔰🔰927.三等分
https://leetcode.cn/problems/three-equal-parts/
class Solution {
public:
vector<int> threeEqualParts(vector<int>& arr) {
int total = 0, len = arr.size();
for(int i = 0 ; i < len; i ++) {
if(arr[i]) total ++;
}
if(total == 0) return {0, 2};
if(total % 3 != 0) return {-1, -1};
int avg = total / 3;
int f[6] = {1, avg, avg + 1, 2*avg, 2*avg + 1, total};
int site[6];
int cnt1 = 0;
for(int i = 0, j = 0 ; i < len; i ++) {
if(!arr[i]) continue;
cnt1 ++;
while(j < 6 && cnt1 == f[j]) site[j ++] = i; //
}
int cnt0 = len - 1 - site[5];
// 判斷前兩段尾部零是否夠用
if(site[2]-site[1]-1 < cnt0 || site[4]-site[3]-1 < cnt0) return {-1, -1};
// 判斷三段是否完全相同
if(!judge(arr, site[0], site[1], site[2], site[3])) return {-1, -1};
if(!judge(arr, site[0], site[1], site[4], site[5])) return {-1, -1};
return {site[1]+cnt0, site[3]+cnt0+1};
}
bool judge(vector<int>& arr, int l1, int r1, int l2, int r2){
for(int i = l1, j = l2; i <= r1; i ++, j ++)
if(arr[i]^arr[j])
return false;
return true;
}
};
🔰🔰811.子域名通路計數
https://leetcode.cn/problems/subdomain-visit-count/
class Solution {
public:
vector<string> subdomainVisits(vector<string>& cpdomains) {
unordered_map<string, int> cnt;
for(auto& t : cpdomains)
{
int a = t.find(' ');
int num = stoi(t.substr(0, a));
t = t.substr(a + 1);
while(1)
{
cnt[t] += num;
a = t.find('.');
if(a == -1) break;
t = t.substr(a + 1);
}
}
vector<string> res;
for(auto& [str, n]:cnt)
{
res.push_back(to_string(n) + ' ' + str);
}
return res;
}
};
🔰🔰921.使括号有效的最少添加
https://leetcode.cn/problems/minimum-add-to-make-parentheses-valid/
class Solution {
public:
int minAddToMakeValid(string s) {
int res = 0, v = 0;
for(auto& t : s)
{
v += t == '(' ? 1 : -1;
// 保持v為非負數
if(v < 0) v = 0, res ++;
}
return res + v;
}
};
🔰🔰287.尋找重複數
https://leetcode.cn/problems/find-the-duplicate-number
法一: 原地哈希
将
nums[i]
存儲在
nums[nums[i]-1]
- 若i == nums[i]-1, 則遊标向後移動
- 若
,則有重複nums[i] = nums[nums[i]-1]
- 若
,則交換nums[i] != nums[nums[i]-1]
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i < n; )
{
int site = nums[i]-1;
if(site == i) {
i ++;
continue;
}
if(nums[site] == nums[i]) return nums[i];
else swap(nums[site], nums[i]);
}
return -1;
}
};
🔰🔰(貪心)870.優勢洗牌
https://leetcode.cn/problems/advantage-shuffle/
思路:
和田忌賽馬差不多, 這裡麻煩在于nums2保持不變
将nums1數組的最小值與nums2數組的最小值相比較
- 如果前者更大, 則将該數放到nums2數組的最小值所處的位置
- 如果後者更大, 則将該數放到nums2數組的最大值所處的位置(采用破罐子好摔的戰略,即我連 你小的數都比不過,那我臨走前要和你大的數比,而我的大數養精蓄銳)
class Solution {
public:
vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
vector<int> res(n);
vector<pair<int, int>> v2(n);
for(int i = 0; i < n; i ++) v2[i] = {nums2[i], i};
sort(v2.begin(), v2.end());
sort(nums1.begin(), nums1.end());
int l = 0, r = n-1;
for(int i = 0; i < n ; i ++)
{
int site = nums1[i] > v2[l].first ? v2[l++].second : v2[r--].second;
res[site] = nums1[i];
}
return res;
}
};
🔰🔰856.括号的分數
https://leetcode.cn/problems/score-of-parentheses
class Solution {
public:
int scoreOfParentheses(string s) {
int res = 0, x = 0, len = s.size();
for(int i = 0; i < len; i ++)
{
if(s[i] == '(') x ++;
else{
x --;
res += (s[i-1] == '(') ? 1 << x : 0;
}
}
return res;
}
};
🔰🔰🔰(狀壓DP)801.使序列遞增的最小交換次數
https://leetcode.cn/problems/minimum-swaps-to-make-sequences-increasing/思路:
f[i][j]
=> 在[0, i]區間内的最小交換次數, j=0/1表示第i個位置是否交換
f[0][0] = 0, f[0][1] = 1
可分為如下兩種情況更新f[][]
-
nums1[i]>nums1[i-1] && nums2[i]>nums2[i-1]
-
f[i][0] = min{f[i][0], f[i-1][0]}
-
f[i][1] = min{f[i][1], f[i-1][1]+1}
-
nums1[i]>nums2[i-1] && nums2[i]>nums1[i-1]
-
f[i][0] = min{f[i][0], f[i-1][1]}
-
f[i][1] = min{f[i][1], f[i-1][0]+1}
class Solution {
public:
int minSwap(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int f[n+5][2];
memset(f, 0x3f, sizeof(f));
f[0][0] = 0, f[0][1] = 1;
for(int i = 1; i < n; i ++)
{
if(nums1[i]>nums1[i-1] && nums2[i]>nums2[i-1]){
f[i][0] = min(f[i][0], f[i-1][0]);
f[i][1] = min(f[i][1], f[i-1][1]+1);
}
if(nums1[i]>nums2[i-1] && nums2[i]>nums1[i-1]){
f[i][0] = min(f[i][0], f[i-1][1]);
f[i][1] = min(f[i][1], f[i-1][0]+1);
}
}
return min(f[n-1][0], f[n-1][1]);
}
};
🔰🔰53.最大子數組和
https://leetcode.cn/problems/maximum-subarray/
前面的累加若是大于零,則對最終的最大值是有貢獻的
若是小于零, 則可以不考慮, 從目前位置重新加起
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
int res = INT_MIN, t = -1;
for(int i = 0; i < n; i ++)
{
t = (t < 0) ? nums[i] : nums[i]+t;
res = (res > t) ? res : t;
}
return res;
}
};
🔰1790.僅執行一次字元串交換能否使兩個字元串相等
https://leetcode.cn/problems/check-if-one-string-swap-can-make-strings-equal/ 逐個比對同一位置上的字元, 若不同, 記錄下目前位置. 并且cnt++
- cnt > 2, ✖
- cnt = 0, ✔
- cnt = 1, ✖
- cnt = 2
-
, ✔s1[ch[0]] == s2[ch[1]] && s1[ch[1]] == s2[ch[0]]
- ✖
class Solution {
public:
bool areAlmostEqual(string s1, string s2) {
int n = s1.size();
int cnt = 0, ch[105];
for(int i = 0; i < n; i ++)
{
if(s1[i]!=s2[i]) ch[cnt++] = i;
if(cnt == 3) return false;
}
if(cnt == 0) return true;
if(cnt == 1) return false;
if(s1[ch[0]] == s2[ch[1]] && s1[ch[1]] == s2[ch[0]]) return true;
return false;
}
};
🔰🔰131.分割回文串
https://leetcode.cn/problems/palindrome-partitioning/
遞歸回溯
class Solution {
public:
int len;
vector<string> path;
vector<vector<string>> res;
vector<vector<string>> partition(string s) {
len = s.size();
dfs(0, s);
return res;
}
void dfs(int u, string s) {
if(u == len){
res.push_back(path);
return ;
}
for(int i = u; i < len; i ++) {
if(check(s, u, i)) {
path.push_back(s.substr(u, i+1-u));
dfs(i+1, s);
path.pop_back();
}
}
}
//判斷從l到r是否為回文串
bool check(string s, int l, int r) {
while(l <= r) {
if(s[l ++] != s[r --]) return false;
}
return true;
}
};
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM3ADNyMjNjRDN1IWN1UjNzYzXxATMxIDM1AzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
通過dp預處理優化
class Solution {
public:
int len;
bool st[20][20];
vector<string> path;
vector<vector<string>> res;
vector<vector<string>> partition(string s) {
len = s.size();
dp(s);
dfs(0, s);
return res;
}
void dfs(int u, string s) {
if(u == len){
res.push_back(path);
return ;
}
for(int i = u; i < len; i ++) {
if(st[u][i]) {
path.push_back(s.substr(u, i+1-u));
dfs(i+1, s);
path.pop_back();
}
}
}
void dp(string s)
{
memset(st, false, sizeof st);
for(int i = 0; i < len; i ++) st[i][i] = true;
for(int i = 0; i < len-1; i ++) {
if(s[i] == s[i+1]) st[i][i+1] = true;
}
for(int k = 3; k <= len; k ++) {
for(int l = 0; l + k - 1 < len; l ++) {
int r = l + k - 1;
if(s[l] == s[r] && st[l+1][r-1]) st[l][r] = true;
}
}
}
};
🔰🔰5.最長回文子串
https://leetcode.cn/problems/longest-palindromic-substring/
🔰🔰817.連結清單元件
https://leetcode.cn/problems/linked-list-components/
解題思路
模拟, 哈希
大緻過程如下
左遊标pl, 右遊标pr
若pl所在的結點值存在,
res++
pr從pl開始向右移動直到
就停止
為空或者值不存在
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
int numComponents(ListNode* head, vector<int>& nums) {
int n = nums.size(), res = 0;
bool st[10010];
memset(st, false, sizeof st);
for(int i = 0; i < n; i ++) st[nums[i]] = true;
for(ListNode* pl = head, *pr = nullptr; pl != nullptr; )
{
if(st[pl->val])
{
res ++;
pr = pl->next;
while(pr != nullptr && st[pr->val]) pr = pr->next;
if(pr) pl = pr->next;
else break;
}
else pl = pl->next;
}
return res;
}
};
🔰🔰769.最多能完成排序的塊
https://leetcode.cn/problems/max-chunks-to-make-sorted/
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
int n = arr.size();
int res = 0, _max = -1, _min = 100;
for(int i = 0, j = 0; i < n; )
{
_max = max(_max, arr[j]);
_min = min(_min, arr[j]);
if(_max == j && _min == i)
{
res ++;
_max = -1, _min = 100;
i = j + 1;
}
j ++;
}
return res;
}
};
其實隻用最大值就可以
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
int n = arr.size();
int res = 0, _max = -1;
for(int i = 0; i < n; i ++) {
_max = max(_max, arr[i]);
if(_max == i) {
res ++;
_max = -1;
}
}
return res;
}
};
🔰🔰🔰940.不同的子序列 II
f[i] : 以目前字母結尾的子序列數量
last[i] : 字母’a’+i 最後出現的位置
對于每一種字元,我們隻需要找到其最後一次出現的位置(并且在位置 i 之前),并将對應的 f 值累加進 f[i] 即可
class Solution {
public:
int distinctSubseqII(string s) {
int n = s.size();
int MOD = 1e9+7, res = 0;
int last[26], f[2010];
for(int i = 0; i < 26; i ++) last[i] = -1;
for(int i = 0; i < n; i ++) f[i] = 1;
for(int i = 0; i < n ; i ++)
{
for(int j = 0; j < 26; j ++)
if(last[j] != -1)
f[i] = (f[i] + f[last[j]])%MOD;
last[s[i] - 'a'] = i;
}
for(int i = 0; i < 26; i ++)
if(last[i] != -1)
res = (res + f[last[i]]) % MOD;
return res;
}
};
初步優化
上面寫法中過程中的f[i]表示以目前字母結尾的子序列數量
不妨用g[i] 表示目前情況下, 以'a'+i 字母結尾的子序列數量總數
最後累加g[]即可
class Solution {
public:
int distinctSubseqII(string s) {
int n = s.size();
int MOD = 1e9+7, res = 0;
int g[26];
for(int i = 0; i < 26; i ++) g[i] = 0;
for(int i = 0; i < n ; i ++)
{
int sum = 1;
for(int j = 0; j < 26; j ++)
sum = (sum + g[j]) % MOD;
g[s[i] - 'a'] = sum;
}
for(int i = 0; i < 26; i ++)
res = (res + g[i]) % MOD;
return res;
}
};
🔰🔰1441.用棧操作建構數組
https://leetcode.cn/problems/build-an-array-with-stack-operations/ 模拟
class Solution {
public:
vector<string> buildArray(vector<int>& target, int n) {
int len = target.size();
vector<string> res;
if(target[0]==1) res.push_back("Push");
else{
for(int j = 0; j < target[0]-1; j ++){
res.push_back("Push");
res.push_back("Pop");
}
res.push_back("Push");
}
for(int i = 1; i < len; i ++)
{
if(target[i]-target[i-1] == 1) res.push_back("Push");
else{
int d = target[i]-target[i-1]-1;
for(int j = 0; j < d; j ++){
res.push_back("Push");
res.push_back("Pop");
}
res.push_back("Push");
}
}
return res;
}
};
🔰🔰🔰6207.統計定界子數組的數目
https://leetcode.cn/problems/count-subarrays-with-fixed-bounds/
滑動數組
更新最大最小值所在的位置, 在該區間之前(直到不滿足條件的數值)的數的個數,
即為定界子數組的數量
class Solution {
public:
long long countSubarrays(vector<int>& nums, int minK, int maxK) {
int n = nums.size();
int imin = -1, imax = -1, iout = -1;
long long res = 0L;
for(int i = 0; i < n; ++i)
{
if(nums[i] == minK) imin = i;
if(nums[i] == maxK) imax = i;
if(nums[i] < minK || nums[i] > maxK) iout = i;
res += max(min(imin, imax)-iout, 0);
}
return res;
}
};
🔰🔰886.可能的二分法
https://leetcode.cn/problems/possible-bipartition/
二分圖, 染色法
dfs
借助dislikes[]構造邊, 相鄰的兩點不應在同一集合, 若在染色的過程中出現沖突的情況, 則無法配置設定成功
class Solution {
public:
vector<int> color;
vector<vector<int>> g;
bool dfs(int u, int cl) {
color[u] = cl;
for(int ne: g[u]) {
if(color[ne] == cl) return false;
// 如果未分組, 且被分到另一組後不合理
if(color[ne] == 0 && !dfs(ne, 3-cl)) return false;
}
return true;
}
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
color.resize(n);
g.resize(n);
for(auto& p: dislikes) {
int a = p[0]-1, b = p[1]-1;
g[a].push_back(b);
g[b].push_back(a);
}
for(int i = 0; i < n; i ++)
{
if(!color[i] && !dfs(i, 1))
return false;
}
return true;
}
};
時間複雜度:
O(n+m)
空間複雜度:
O(n+m)
并查集
class Solution {
public:
vector<int> p;
void add(int a, int b) {
p[find(a)] = p[find(b)];
}
int find(int x) {
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
bool query(int a, int b) {
return find(a) == find(b);
}
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
p.resize(n*2+10);
for(int i = 1; i <= n*2; i ++) p[i] = i;
for(auto& t : dislikes){
int a = t[0], b = t[1];
if(query(a, b)) return false;
add(a, b+n);
add(b, a+n);
}
return true;
}
};
時間複雜度:
O(n+m)
空間複雜度:
O(n)
🔰🔰904.水果成籃
https://leetcode.cn/problems/fruit-into-baskets/
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
int res = 0;
unordered_map<int, int> mp;
for(int i = 0, j = 0; j < n; j ++)
{
++mp[fruits[j]];
while(mp.size() > 2){
int x = fruits[i++];
if(--mp[x] == 0) mp.erase(x);
}
res = max(res, j-i+1);
}
return res;
}
};
優化
🔰🔰🔰902.最大為 N 的數字組合
https://leetcode.cn/problems/numbers-at-most-n-given-digit-set/
dfs, 逾時
class Solution {
public:
int num;
int atMostNGivenDigitSet(vector<string>& digits, int n) {
num = 0;
dfs(digits, 0, n, num);
return num;
}
void dfs(vector<string>& digits, int u, int n, int num) {
if(u > n) return ;
if(u && u <= n) ++num;
for(auto& d : digits) {
dfs(digits, u*10+d[0]-'0', n, num);
}
}
};
數位DP
>>>靈茶山艾府 - 數位 DP 通用模闆
class Solution {
public:
int atMostNGivenDigitSet(vector<string>& digits, int n) {
string s = to_string(n);
int len = s.size(), dp[len];
memset(dp, -1, sizeof(dp));
function<int(int, bool, bool)> f = [&](int u, bool is_limit, bool is_num) -> int {
if(u == len) return is_num; //方案數, 0 or 1
if(!is_limit && is_num && dp[u] >= 0) return dp[u];
int res = 0;
if(!is_num) res = f(u+1, false, false);
char up = is_limit ? s[u] : '9';
for(auto& d : digits) {
if(d[0] > up) break;
res += f(u+1, is_limit && d[0] == up, true);
}
if(!is_limit && is_num) dp[u] = res;
return res;
};
return f(0, true, false);
}
};
🔰1700.無法吃午餐的學生數量
https://leetcode.cn/problems/number-of-students-unable-to-eat-lunch/
class Solution {
public:
int countStudents(vector<int>& students, vector<int>& sandwiches) {
int n = students.size();
int cnt[2] = {0, 0};
for(int i = 0; i < n; i ++) cnt[students[i]] ++;
for(int i = 0; i < n; i ++)
if(cnt[sandwiches[i]]-- == 0)
return cnt[1-sandwiches[i]]; //return cnt[sandwiches[i]^1];
return 0;
}
};
🔰🔰779.第K個文法符号
https://leetcode.cn/problems/k-th-symbol-in-grammar/
遞歸模拟
class Solution {
public:
int kthGrammar(int n, int k) {
if(n == 1) return 0;
if(k <= (1<<(n-2))) return kthGrammar(n-1, k);
return kthGrammar(n-1, k-(1<<(n-2)))^1;
}
};
✨位運算, 奇偶校驗
【位操作筆記】計算奇偶性 使用乘法
class Solution {
public:
int kthGrammar(int N, unsigned int K) {
K -= 1;
K ^= K >> 1;
K ^= K >> 2;
K = (K & 0x11111111U) * 0x11111111U;
return (K >> 28) & 1;
}
};
(單調棧)🔰🔰901.股票價格跨度
https://leetcode.cn/problems/online-stock-span/
class StockSpanner {
public:
StockSpanner() {
}
int next(int price) {
int res = 1;
while(!st.empty() && st.top().first <= price) {
res += st.top().second;
st.pop();
}
st.push({price, res});
return res;
}
private:
stack<pair<int ,int>> st;
};
/**
* Your StockSpanner object will be instantiated and called as such:
* StockSpanner* obj = new StockSpanner();
* int param_1 = obj->next(price);
*/
🔰🔰🔰1235.規劃兼職工作
https://leetcode.cn/problems/maximum-profit-in-job-scheduling/
動态規劃
class Jobs{
public:
int startTime;
int endTime;
int profit;
Jobs(){
}
Jobs(int a, int b, int c):startTime(a), endTime(b), profit(c){
}
};
class Solution {
public:
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
int n = startTime.size();
vector<Jobs> jobs(n);
for(int i = 0; i <n; ++i)
jobs[i] = Jobs(startTime[i], endTime[i], profit[i]);
//按截止時間排序
sort(jobs.begin(), jobs.end(), [](const Jobs& a, const Jobs& b) -> bool{
return a.endTime < b.endTime;
});
for(int i = 0; i < n; ++i) {
int maxProfit = jobs[i].profit;
for(int j = i - 1; j >= 0; --j) {
if(jobs[j].endTime <= jobs[i].startTime){
maxProfit = max(maxProfit, jobs[j].profit+jobs[i].profit);
break;
}else{
maxProfit = max(maxProfit, jobs[j].profit);
}
}
jobs[i].profit = maxProfit;
}
return jobs[n-1].profit;
}
};
二分優化
🔰1768.交替合并字元串
https://leetcode.cn/problems/merge-strings-alternately/
class Solution {
public:
string mergeAlternately(string word1, string word2) {
int n = word1.size(), m = word2.size();
int i = 0, j = 0;
string res ;
while(i < n && j < m)
{
if(i < n) res += word1[i++];
if(j < m) res += word2[j++];
}
while(i < n) {
res += word1[i++];
}
while(j < m) {
res += word2[j++];
}
return res;
}
};
🔰🔰915.分割數組
https://leetcode.cn/problems/partition-array-into-disjoint-intervals/ 從右往左初始化rightmin(目前位置右邊的最小值)數組
再從左往右掃描即可
class Solution {
public:
int partitionDisjoint(vector<int>& nums) {
int n = nums.size();
int rightmin[n+5];
int tmin = 1e6+10;
for(int i = n - 2; i >= 0; --i){
rightmin[i] = tmin < nums[i+1] ? tmin : nums[i+1];
tmin = rightmin[i];
}
int res, tmax = -1;
for(int i = 0; i < n; ++i){
tmax = tmax > nums[i] ? tmax: nums[i];
if(tmax <= rightmin[i]){
res = i+1;
break;
}
}
return res;
}
};
bfs+dfs 🔰🔰934.最短的橋
https://leetcode.cn/problems/shortest-bridge/
#define x first
#define y second
typedef pair<int ,int> PII;
class Solution {
public:
int n, m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
queue<PII> q;
vector<vector<int>> g, dist;
// 将一座島置為0, 并加入隊列
void dfs(int x, int y) {
g[x][y] = 0;
dist[x][y] = 0;
q.push({x, y});
for(int i = 0; i < 4; ++i) {
int nx = x + dx[i], ny = y + dy[i];
if(nx < 0 || nx >= n || ny < 0 || ny >= m || !g[nx][ny]) continue;
dfs(nx, ny);
}
}
int bfs() {
while(q.size()) {
PII t = q.front();
q.pop();
for(int i = 0; i < 4; ++i){
int nx = t.x + dx[i], ny = t.y + dy[i];
if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
// 待更新
if(dist[t.x][t.y] + 1 >= dist[nx][ny]) continue;
dist[nx][ny] = dist[t.x][t.y] + 1;
if(g[nx][ny]) return dist[nx][ny]-1;
q.push({nx, ny});
}
}
return -1;
}
int shortestBridge(vector<vector<int>>& grid) {
n = grid.size(), m = grid[0].size();
g = grid;
dist = vector<vector<int>>(n, vector<int>(m, 1e6));
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
if(grid[i][j]){
dfs(i, j);
return bfs();
}
return -1;
}
};
🔰🔰560.和為 K 的子數組
https://leetcode.cn/problems/subarray-sum-equals-k/
- 如果利用字首和數組, 再二重循環暴力求解, 時間複雜度為O(n^2)
- 優化寫法: 在每次累加字首和後, 判斷是否前面曾出現一個字首和
, 即此時存在和為k的子數組在中間夾着. 時間複雜度為O(n)pre_sum = sum - k
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int n = nums.size();
int cnt = 0, sum = 0;
unordered_map<int, int> mp;
mp[0] = 1;
for(int i = 0; i < n; ++i) {
sum += nums[i];
cnt += mp[sum-k];
++mp[sum];
}
return cnt;
}
};
🔰🔰🔰862.和至少為 K 的最短子數組
https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/
暴力解法, 逾時
時間複雜度:O(n^2)
空間複雜度:O(n)
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
int n = nums.size();
int sum[n+10];
sum[0] = 0;
for(int i = 0; i < n; ++i) {
if(nums[i] >= k) return 1;
}
for(int i = 0; i < n; ++i) {
sum[i+1] = sum[i]+nums[i];
}
int res = 1e5+10;
for(int i = 1; i <= n; ++i) {
for(int j = i+1; j <= n; ++j) {
if(sum[j] - sum[i-1] >= k)
res = min(res, j-i+1);
}
}
return res == 1e5+10 ? -1:res;
}
};
雙端隊列
在上面的雙層循環中,還有一定的優化空間
比如當sum[x1] >= sum[x2](x1 < x2)時,表明x1到x2之間的元素的和是負數或0,則sum[xn] - sum[x1] >= K時,那麼這個時候我們隻計算xn - x2即可(
必有sum[xn] - sum[x2] >= K
x1到x2之間的元素可以全部跳過
),就不需要計算xn - x1了,因為後者一定是更大的,不滿足我們要選最小的條件。
另一個角度,當sum[x2] - sum[x1] >= K時,x1就可以跳過了,為什麼呢?因為x1到x2已經滿足了大于K,再繼續從x1開始向後再找,也不會再有比x2距離x1更近的了,畢竟我們要求的是最小的x2 - x1。
以上的兩種分析,情況1是把位于末尾沒用的x1扔掉,情況2是把指向前面的已經滿足條件的x1的指針向後移動1位,這是就需要比較目前最小值與此時剛符合條件的值的大小了。
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
int n = nums.size();
long sum[n+10];
sum[0] = 0;
for(int i = 0; i < n; ++i) {
if(nums[i] >= k) return 1;
}
for(int i = 0; i < n; ++i) {
sum[i+1] = sum[i]+nums[i];
}
long res = 1e5+10;
deque<int> dq;
for(int i = 0; i <= n; ++i) {
while(!dq.empty() && sum[i] <= sum[dq.back()]) {
dq.pop_back();
}
while(!dq.empty() && sum[i] - sum[dq.front()] >= k) {
int len = i - dq.front();
dq.pop_front();
res = res < len ? res : len;
}
dq.push_back(i);
}
return res == 1e5+10 ? -1:res;
}
};
(單調棧)🔰🔰907.子數組的最小值之和
https://leetcode.cn/problems/sum-of-subarray-minimums/ 參考: 0x3f 貢獻法+單調棧 利用單調棧初始化left[]和right[]
class Solution {
const int MOD = 1e9+7;
public:
int sumSubarrayMins(vector<int>& arr) {
int n = arr.size();
vector<int> left(n, -1);
vector<int> right(n, n);
int stk[n], k = -1;
for(int i = 0; i < n; ++i) {
while(k != -1 && arr[stk[k]] >= arr[i]) --k;
if(k != -1) left[i] = stk[k];
stk[++k] = i;
}
k = -1;
for(int i = n-1; i >= 0; --i) {
while(k != -1 && arr[stk[k]] > arr[i]) --k;
if(k != -1) right[i] = stk[k];
stk[++k] = i;
}
long res = 0L;
for(int i = 0; i < n; ++i) {
res += (long)arr[i]*(i-left[i])*(right[i]-i);
}
return res%MOD;
}
};
在更新left時,可以同時更新right
對于棧頂元素 t,如果 t 右側有多個小于或等于 t 的元素,那麼 t 隻會因為右側第一個小于或等于 t 的元素而出棧,這恰好符合右邊界的定義
class Solution {
const int MOD = 1e9+7;
public:
int sumSubarrayMins(vector<int>& arr) {
int n = arr.size();
vector<int> left(n, -1);
vector<int> right(n, n);
int stk[n], k = -1;
for(int i = 0; i < n; ++i) {
while(k != -1 && arr[stk[k]] >= arr[i]) {
right[stk[k]] = i;
--k;
}
if(k != -1) left[i] = stk[k];
stk[++k] = i;
}
long res = 0L;
for(int i = 0; i < n; ++i) {
res += (long)arr[i]*(i-left[i])*(right[i]-i);
}
return res%MOD;
}
};
class Solution {
const int MOD = 1e9+7;
public:
int sumSubarrayMins(vector<int>& arr) {
arr.push_back(-1); //右哨兵
int n = arr.size();
int stk[n], k = -1;
long res = 0L;
stk[++k] = -1; //左哨兵
for(int r = 0; r < n; ++r) {
while(k > 0 && arr[stk[k]] >= arr[r]) {
int t = stk[k--];
res += (long) arr[t]*(t - stk[k])*(r - t);
}
stk[++k] = r;
}
return res%MOD;
}
};