LeetCode第215場周賽
第一題 1656. 設計有序流
可以開一個字串陣列存放字串,用一個額外的變數ptr存放ptr的位置,插入新的字串的時候,檢查從ptr開始是否有連續的按照id遞增的序列即可。
程式碼如下:
class OrderedStream {
public:
int n;
int ptr;
vector<string> stream;
OrderedStream(int _n) {
n = _n;
ptr = 1;
stream = vector<string>(n + 1, ""); // 字串陣列中所有字串最初都為空
}
vector<string> insert(int id, string value) {
stream[id] = value;
if(stream[ptr] == "") { // 如果插入一個新字串,但id=ptr的字串為空,則返回一個空的字串陣列
return {};
}
vector<string> res;
int last = ptr; // last記錄更新後的ptr的位置
for(int i = ptr; i <= n; ++i) {
if(stream[i] != "") {
res.push_back(stream[i]);
} else { // 找到一個空的位置,則更新last
last = i;
break;
}
}
if(stream[last] != "") {
ptr = last + 1;
} else {
ptr = last;
}
return res;
}
};
/**
* Your OrderedStream object will be instantiated and called as such:
* OrderedStream* obj = new OrderedStream(n);
* vector<string> param_1 = obj->insert(id,value);
*/
第二題 5603. 確定兩個字串是否接近
根據題意,兩個字串接近,則滿足這些條件:(1)兩個字串中不同的字母的個數相同;(2)如果給所有的字母統計出現次數,則如果某字串中有出現次數為x的字母y個,則另一個字串也必然有y個出現次數為x的字母;(3)第一個字串中的所有字母都在第二個單詞內出現過,反之亦然。
第(1)和第(3)點我們可以使用兩個unorderd_set進行判斷。
第(2)點可以用兩個unordered_map<char, int> cnt1, cnt2統計兩個字串中各個字母出現的頻率,然後用兩個字串(或陣列)將雜湊表中的所有頻率拼接為一個字串,如果原單詞word1和word2是“接近”的字串,則將它們的字母出現頻率拼接成的字串排序之後,這兩個排序後的字串必然是相等的,如果不相等,則它們不接近。這一點很好理解,比如word1裡有若干個單詞出現次數為:2 1 3 4, word2裡有若干個單詞出現次數為:1 2 4 3,那麼它們的出現次數拼接的單詞排序之後都是1234,所以這兩個單詞是接近的。
程式碼如下:
class Solution {
public:
unordered_set<char> hash1, hash2; // 計算兩個單詞中不同單詞的個數
unordered_map<char, int> cnt1, cnt2; // 統計兩個單詞中不同字母出現的次數
bool closeStrings(string word1, string word2) {
if(word1.size() != word2.size()) { // 如果兩個單詞長度不同,那麼它們不接近
return false;
}
for(auto &c : word1) {
hash1.insert(c);
++cnt1[c];
}
for(auto &c : word2) {
hash2.insert(c);
++cnt2[c];
}
for(auto &c : hash1) {
if(hash2.count(c) == 0) { // 如果第一個單詞有第二個單詞沒有出現過的字母,則它們不接近
return false;
}
}
for(auto &c : hash2) {
if(hash1.count(c) == 0) { // 如果第二個單詞有第一個單詞沒有出現過的字母,則它們不接近
return false;
}
}
string fre1, fre2; // fre1和fre2分別是對word1和word2的單詞出現的頻率拼接得到的字串
for(auto &cnt : cnt1) {
fre1 += to_string(cnt.second);
}
for(auto &cnt : cnt2) {
fre2 += to_string(cnt.second);
}
sort(fre1.begin(), fre1.end());
sort(fre2.begin(), fre2.end());
if(fre1 != fre2) {
return false;
}
return true;
}
};
第三題 5602. 將 x 減到 0 的最小運算元
題意要求在陣列的兩端不斷地刪除元素,使得刪掉的元素的和為x,求最小的刪除次數。可以反向思考:求陣列中最長的區間和為sum - x的區間,其中sum為陣列中所有元素的和。
求陣列中的區間和可以使用字首和,然而列舉區間的起點和終點時間複雜度仍會達到O(n2),對於105的區間長度是無法接受的。
因此我們可以考慮用一個雜湊表unordered_map<int, int> mp來記錄某個字首和的下標,mp[i] = j表示字首和為i的下標為j,即nums[0] + nums[1] + … nums[j] = i, 我們要找出區間和target = sum - x的區間,而我們在迴圈的時候可以知道當前位置i的字首和preSum[i], 因此我們只要知道字首和為preSum[i] - target的位置,我們就得到了滿足題意的一段區間和(也就是說,刪掉這段區間的左邊部分和右邊部分,可以滿足題意),然後我們可以用陣列元素的個數 - 這段區間的長度來更新答案。 使用雜湊表記錄字首和的下標,我們可以在O(n)的時間複雜度內求出滿足條件的區間和和區間長度。
程式碼如下:
class Solution {
public:
vector<int> preSum;
unordered_map<int, int> mp; // 記錄某字首和的下標
int minOperations(vector<int>& nums, int x) {
if(nums[0] > x && nums.back() > x) { // 如果陣列的第一個和最後一個元素都大於x,返回-1
return -1;
}
int n = nums.size();
preSum = vector<int>(n + 1, 0);
int res = n + 1;
mp[0] = 0; // 0的字首和為0
for(int i = 1; i <= n; ++i) {
preSum[i] = preSum[i - 1] + nums[i - 1];
mp[preSum[i]] = i; // i的字首和為preSum[i]
}
int sum = preSum[n]; // sum是陣列的所有元素的和
if(x > sum) {
return -1;
}
if(sum == x) { // 刪掉所有元素
return n;
}
int target = sum - x; // 要求的區間和target為sum - x
for(int i = 1; i <= n; ++i) {
if(mp[preSum[i] - target] == 0) { // 之前沒記錄過字首和為preSum[i] - left的位置
if(preSum[i] == target) { // 如果preSum[i]正好就是target,那麼說明我們要刪掉i後面的所有元素,可以滿足刪掉的元素的和為x
res = min(res, n - i);
}
} else { // 之前記錄過字首和preSum[i] - target的位置,也就是說,我們找到了一個滿足條件的區間[left ~ i]
int left = mp[preSum[i] - target];
res = min(res, n - (i - left)); // 更新最小運算元:區間和為sum - x的區間長度為i - left, 則這段區間左邊部分與右邊部分的長度為n - (i - left)
}
}
return res;
}
};
第四題 1659. 最大化網格幸福感
狀態壓縮DP,這裡參考了坑神的視訊題解
用dp[r][i][e][s]表示前r行網格(1<=r<=n)、居住了i個內向的人(0<=i<=introvertsCount)、e個外向的人(0<=e<=extrovertsCount)、並且第r行的居住人的分佈狀態為s的最大網格幸福感,s的三進製表示是第r行的分佈。如果第r行的第j個網格(0<=j<m)沒有住人,則s的第j位為0;如果住了一個內向的人,則s的第j位為1;如果住了一個外向的人,則s的第j位為2。
則整個網格的最大幸福感是,處理了前n行,最多居住introvertsCount個內向的人、最多居住extrovertsCount個外向的人,分佈狀態為s(0<=s<3^m)的網格幸福感的最大值。
有了狀態表示,我們要考慮狀態轉移。
首先,由於我們是按行列舉,所以我們對每一行,都要考慮上一行對這一行的影響(影響指:內向的人數的變化、外向的人數的變化、幸福感的變化)。另外,由於這一行的人員分佈狀態也可能影響上一行幸福感(比如這一行住了人,會影響到上一行的內向的人和外向的人的幸福感),因此我們在列舉某行的狀態和上一行的狀態時,要考慮到互相的影響,具體的分析寫在下面的註釋裡。
程式碼如下:
int dp[7][7][7][250]; // dp[r][i][e][s]:前r行使用了i個內向的人、e個外向的人,分佈狀態為s的最大網格幸福感。 因為一行最多有5個格子,3^5=243,所以分佈狀態s的範圍為0~242
int base[6]; // base表示每一列是3的多少次冪。第0列base[0] = 3^0 = 1, 第1列base[1] = 3^1 = 3, 第2列base[2] = 3^2 = 9, 第3列base[3] = 3^3 = 27,第4列base[4] = 81, 第5列base[5] = 243。 base陣列的目的是為了方便為門計算狀態s的某一位是多少(0/1/2)
int movIn[250][250], movEx[250][250], mov[250][250]; // movIn[i][j]表示從某行的人員分佈狀態i轉移到下一行的人員分佈狀態j內向的人數的變化,movEx[i][j]表示外向的人數的變化,mov[i][j]表示幸福感的變化
class Solution {
public:
int get(int s, int i) { // 計算狀態s的第i位是多少。 返回值=0:沒人 =1:內向的人 =2:外向的人
s = s % base[i + 1];
return s / base[i];
}
int getMaxGridHappiness(int m, int n, int introvertsCount, int extrovertsCount) {
memset(dp, -1, sizeof dp);
dp[0][0][0][0] = 0;
base[0] = 1;
for(int i = 1; i <= 5; ++i) {
base[i] = base[i - 1] * 3;
}
int lim = base[m]; // lim是某一行狀態的最大值+1,也就是說,某一行的狀態s的取值範圍為0 ~ lim - 1
// 預處理出所有狀態到其他狀態造成的movIn, movEx, mov的變化
for(int s = 0; s < lim; ++s) { // 列舉上一行的狀態s
for(int cur = 0; cur < lim; ++cur) { // 列舉當前行的狀態cur
movIn[s][cur] = movEx[s][cur] = mov[s][cur] = 0; // 先假設上一行的狀態轉移到當前行的狀態,內向的人數、外向的人數、幸福感都沒有變化
for(int i = 0; i < m; ++i) { // 列舉當前行的每一列
if(get(cur, i) == 0) { // 如果當前行的第i列沒有住人
continue; // 那麼它對上一行沒有任何影響
} else {
if(get(s, i) == 1) { // 如果當前位置住了人並且上一行的這個位置住了個內向的人,那麼幸福感-30
mov[s][cur] -= 30;
} else if(get(s, i) == 2) { // 如果當前位置住了人並且上一行的這個位置住了個外向的人,那麼幸福感+20
mov[s][cur] += 20;
}
}
if(get(cur, i) == 1) { // 如果當前位置住了個內向的人
movIn[s][cur] += 1; // 更新內向的人數的變化
mov[s][cur] += 120; // 內向的人的初始幸福感是120
if(get(s, i) > 0) { // 如果上一行這一列位置住了人,那麼幸福感-30
mov[s][cur] -= 30;
}
if(i > 0 && get(cur, i - 1) > 0) { // 如果左邊位置住了人,幸福感-30
mov[s][cur] -= 30;
}
if(i + 1 < m && get(cur, i + 1) > 0) { // 如果右邊位置住了人,幸福感-30
mov[s][cur] -= 30;
}
}
if(get(cur, i) == 2) { // 如果當前位置住了一個外向的人
movEx[s][cur] += 1; // 更新外向的人數
mov[s][cur] += 40; // 外向的人初始幸福感是40
if(get(s, i) > 0) { // 如果上一行這一列住了個人
mov[s][cur] += 20; // 幸福感+20
}
if(i > 0 && get(cur, i - 1) > 0) { // 如果左邊位置住了人,幸福感+20
mov[s][cur] += 20;
}
if(i + 1 < m && get(cur, i + 1) > 0) { // 如果右邊位置住了人,幸福感+20
mov[s][cur] += 20;
}
}
}
}
}
// 動態規劃
for(int r = 1; r <= n; ++r) { // 列舉1~n行
for(int i = 0; i <= introvertsCount; ++i) { // 列舉內向的人的個數
for(int e = 0; e <= extrovertsCount; ++e) { // 列舉外向的人的個數
for(int s = 0; s < lim; ++s) { // 列舉上一行(第r - 1行)的狀態s
if(dp[r - 1][i][e][s] == -1) {
continue;
}
for(int cur = 0; cur < lim; ++cur) { // 列舉當前行的狀態cur
int deltaIn = movIn[s][cur], deltaEx = movEx[s][cur], delta = mov[s][cur]; // deltaIn, deltaEx, delta表示上一行的狀態s到這一行的狀態cur引起的內向的人數、外向的人數、幸福感的變化
if(i + deltaIn <= introvertsCount && e + deltaEx <= extrovertsCount) { // 如果內向的人數和外向的人數的個數分別不超過introvertsCount和extrovertsCount,則更新最大幸福感
dp[r][i + deltaIn][e + deltaEx][cur] = max(dp[r][i + deltaIn][e + deltaEx][cur], dp[r - 1][i][e][s] + delta);
}
}
}
}
}
}
int ans = 0; // 我們要求出最後一行(第n行)的最大幸福感,就是最終的答案
for(int i = 0; i <= introvertsCount; ++i) { // 列舉內向的人的個數、外向的人的個數、以及第n行的人員分佈狀態
for(int e = 0; e <= extrovertsCount; ++e) {
for(int s = 0; s < lim; ++s) {
ans = max(ans, dp[n][i][e][s]);
}
}
}
return ans;
}
};
相關文章
- leetcode 第 217 場周賽(vivo)LeetCode
- [leetcode 第 400 場周賽]題解LeetCode
- Leetcode 第136場周賽解題報告LeetCode
- Leetcode第 217 場周賽(思維量比較大)LeetCode
- LeetCode 第 196 場周賽 (題目:5452-5455,這是參加過最坑的周賽,暴力n^2居然可以過)LeetCode
- LeetCode-215-陣列中的第K個最大元素LeetCode陣列
- leetcode周賽 - 406LeetCode
- Leetcode周賽119LeetCode
- 【leetcode 399 周賽】【題解】LeetCode
- 【LEETCODE】模擬面試-215. Kth Largest EleLeetCode面試
- python leetcode 215. Kth Largest Element in an ArrayPythonLeetCode
- 215. 陣列中的第K個最大元素陣列
- 前端工程師的 LeetCode 之旅 -- 周賽 182前端工程師LeetCode
- LeetCode 第 42 場雙週賽 ( rank 514 / 1578 )LeetCode
- 力扣-215. 陣列中的第K個最大元素力扣陣列
- 20241027LeetCode421周賽LeetCode
- 215、陣列中的第K個最大元素 | 演算法(leetcode,附思維導圖 + 全部解法)300題陣列演算法LeetCode
- CSDN周賽第51期:贏《C++ Primer Plus 第6版 中文版》和定製周邊C++
- Leetcode第1~10題LeetCode
- CSDN周賽第48期:贏《MongoDB核心原理與實踐》和定製周邊MongoDB
- CSDN周賽第34期:贏《MongoDB核心原理與實踐》和定製周邊MongoDB
- CSDN周賽第40期:贏副總裁簽名實體書和《VC++深入詳解(第3版)》C++
- 413周賽·第二題 - 3275. 第 K 近障礙物查詢
- 牛客周賽48
- LeetCode 第 69 題 (Sqrt(x))LeetCode
- LeetCode 第 7 題(Reverse Integer)LeetCode
- LeetCode 第 342 題(Power of Four)LeetCode
- LeetCode 第 343 題 (Integer Break)LeetCode
- LeetCode 第 66 題 (Plus One)LeetCode
- LeetCode 第 50 題 (Pow(x, n))LeetCode
- LeetCode 第 9 題(Palindrome Number)LeetCode
- LeetCode 第 190 題 (Reverse Bits)LeetCode
- LeetCode 第 37 題 (Sudoku Solver)LeetCode
- LeetCode 第 326 題 (Power of Three)LeetCode
- LeetCode 第 65 題(Valid Number)LeetCode
- 科技愛好者週刊(第 215 期):網際網路最喜歡的行為模式模式
- 十六週周賽總結
- 牛客周賽 Round 8