灵神力扣题单之单调栈算法

21 阅读31分钟

1 介绍

本博客用来记录灵神力扣题单之单调栈算法

2 训练

2.1 单调栈

题目1739. 每日温度

解题思路:单调栈,下一个更大的数字。

C++代码如下,

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& a) {
        //下一个更大的数字
        int n = a.size();
        vector<int> res(n, 0);
        stack<int> stk;
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && a[stk.top()] <= a[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                res[i] = stk.top() - i;
            }
            stk.push(i);
        }  
        return res;
    }
};

python3代码如下,

class Solution:
    def dailyTemperatures(self, a: List[int]) -> List[int]:
        #下一个更大的数字
        n = len(a)
        res = [0] * n
        stk = [] 
        for i in range(n-1,-1,-1):
            while len(stk) > 0 and a[stk[-1]] <= a[i]:
                del stk[-1]
            if len(stk) > 0:
                res[i] = stk[-1] - i 
            stk.append(i)
        return res 

题目21475. 商品折扣后的最终价格

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    vector<int> finalPrices(vector<int>& a) {
        //下一个更小的数
        int n = a.size();
        vector<int> b(n, 0);
        stack<int> stk;
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && a[stk.top()] > a[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = a[stk.top()];
            }
            stk.push(i);
        }
        vector<int> res(n, 0);
        for (int i = 0; i < n; ++i) {
            res[i] = a[i] - b[i];
        }
        return res;
    }   
};

python3代码如下,

class Solution:
    def finalPrices(self, a: List[int]) -> List[int]:
        #下一个更小的数
        n = len(a)
        b = [0] * n 
        stk = []
        for i in range(n-1,-1,-1):
            while len(stk) > 0 and a[stk[-1]] > a[i]:
                del stk[-1]
            if len(stk) > 0:
                b[i] = a[stk[-1]]
            stk.append(i)
        res = [0] * n
        for i in range(n):
            res[i] = a[i] - b[i]
        return res 
#另一种解法
class Solution:
    def dailyTemperatures(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [0] * n 
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] < nums[i]:
                j = stk[-1]
                res[j] = i - j #nums[j]的下一个更大的温度是nums[i]
                del stk[-1]
            stk.append(i)
        return res   

题目3496. 下一个更大元素 I

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        //nums2中下一个更大的元素
        int n = nums2.size();
        vector<int> a(n, -1);
        stack<int> stk;
        for (int i = n - 1; i >= 0; --i) {
            while (!stk.empty() && nums2[stk.top()] <= nums2[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = nums2[stk.top()];
            }
            stk.push(i);
        }

        unordered_map<int, int> map_val_idx;
        for (int i = 0; i < n; ++i) {
            map_val_idx[nums2[i]] = i;
        }

        int m = nums1.size();
        vector<int> res(m, -1);
        for (int i = 0; i < m; ++i) {
            int idx = map_val_idx[nums1[i]];
            res[i] = a[idx];
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        #nums2中下一个更大的元素
        n = len(nums2)
        a = [-1] * n
        stk = []
        for i in range(n-1,-1,-1):
            while len(stk) > 0 and nums2[stk[-1]] <= nums2[i]:
                del stk[-1]
            if len(stk) > 0:
                a[i] = nums2[stk[-1]]
            stk.append(i)
        
        map_val_idx = collections.defaultdict(int)
        for i in range(n):
            map_val_idx[nums2[i]] = i 
        
        m = len(nums1)
        res = [-1] * m 
        for i in range(m):
            idx = map_val_idx[nums1[i]]
            res[i] = a[idx]
        return res 

题目4503. 下一个更大元素 II

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> a = nums;
        a.insert(a.end(), nums.begin(), nums.end());
        int n = a.size();
        vector<int> res(n, -1);
        stack<int> stk;
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && a[stk.top()] <= a[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                res[i] = a[stk.top()];
            }
            stk.push(i);
        }
        vector<int> ans;
        for (int i = 0; i < n/2; ++i) {
            ans.push_back(res[i]);
        }
        return ans;
    }
};

python3代码如下,

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        nums += nums 
        n = len(nums)
        res = [-1] * n 
        stk = []
        for i in range(n-1,-1,-1):
            while len(stk) > 0 and nums[stk[-1]] <= nums[i]:
                del stk[-1]
            if len(stk) > 0:
                res[i] = nums[stk[-1]]
            stk.append(i)
        return res[0:n//2]

题目51019. 链表中的下一个更大节点

解题思路:单调栈。

C++代码如下,

/**
 * 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:
    vector<int> nextLargerNodes(ListNode* head) {
        vector<int> nums;
        ListNode* node = head;
        while (node != nullptr) {
            nums.push_back(node->val);
            node = node->next;
        }
        int n = nums.size();
        vector<int> res(n, 0);
        stack<int> stk;
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] <= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                res[i] = nums[stk.top()];
            }
            stk.push(i);
        }
        return res;
    }
};

python3代码如下,

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def nextLargerNodes(self, head: Optional[ListNode]) -> List[int]:
        nums = []
        node = head 
        while node is not None:
            nums.append(node.val)
            node = node.next 
        n = len(nums)
        res = [0] * n 
        stk = []
        for i in range(n-1,-1,-1):
            while len(stk) > 0 and nums[stk[-1]] <= nums[i]:
                del stk[-1]
            if len(stk) > 0:
                res[i] = nums[stk[-1]]
            stk.append(i)
        return res 

题目6962. 最大宽度坡

解题思路:只求解最大宽度,并不能求解出每个i对应的最优的j。这是,先按照值严格单调递减的要求,将下标存入stk中。然后逆序遍历,更新答案res

C++代码如下,

class Solution {
public:
    int maxWidthRamp(vector<int>& nums) {
        int n = nums.size();
        stack<int> stk;
        stk.push(0);
        for (int i = 1; i < n; ++i) {
            if (nums[stk.top()] > nums[i]) {
                stk.push(i);
            }
        }
        int res = 0;
        for (int j = n-1; j >= 0; --j) {
            while (!stk.empty() && nums[stk.top()] <= nums[j]) {
                res = max(res, j-stk.top());
                stk.pop();
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maxWidthRamp(self, nums: List[int]) -> int:
        n = len(nums)
        stk = [0]
        for i in range(1,n):
            if len(stk) == 0 or nums[stk[-1]] > nums[i]:
                stk.append(i)
        res = 0
        for j in range(n-1,-1,-1):
            while len(stk) > 0 and nums[stk[-1]] <= nums[j]:
                res = max(res, j-stk[-1])
                del stk[-1]
        return res 

题目7853. 车队

解题思路:距离从大到小排序。如果栈为空,或者栈顶时间<当前车辆时间,将当前车辆时间压入栈中,最终返回栈的长度。

C++代码如下,

class Solution {
public:
    int carFleet(int target, vector<int>& position, vector<int>& speed) {
        vector<pair<int,int>> data;
        int n = position.size();
        for (int i = 0; i < n; ++i) {
            data.push_back(make_pair(position[i],speed[i]));
        }
        sort(data.begin(), data.end(), [](const pair<int,int>& a, const pair<int,int>& b) {
            return a.first > b.first || (a.first == b.first && a.second >= b.second);
        });
        stack<double> stk;
        for (int i = 0; i < n; ++i) {
            double t = (1.0 * target - data[i].first) / data[i].second;
            if (stk.empty() || stk.top() < t) {
                stk.push(t);
            }
        }
        return stk.size();
    }
};

python3代码如下,

class Solution:
    def carFleet(self, target: int, position: List[int], speed: List[int]) -> int:
        data = list(zip(position, speed))
        data.sort(reverse=True)
        stk = []
        for p,s in data:
            t = (target - p) / s 
            if not stk or stk[-1] < t:
                stk.append(t)
        return len(stk)

题目8901. 股票价格跨度

解题思路:单调栈。

C++代码如下,

class StockSpanner {
public:
    StockSpanner() {
        idx = 0;
        stk = stack<pair<int,int>>();
    }
    
    int next(int price) {
        int ans = 1;
        while (!stk.empty() && stk.top().first <= price) {
            stk.pop();
        }
        if (!stk.empty()) {
            ans = idx - stk.top().second;
        } else {
            ans = max(ans, idx + 1);
        }
        stk.push(make_pair(price,idx));
        idx += 1;
        return ans;
    }
private:
    int idx = 0;
    stack<pair<int,int>> stk;
};

/**
 * Your StockSpanner object will be instantiated and called as such:
 * StockSpanner* obj = new StockSpanner();
 * int param_1 = obj->next(price);
 */

python3代码如下,

class StockSpanner:

    def __init__(self):
        self.stk = []
        self.idx = 0


    def next(self, price: int) -> int:
        ans = 1
        while self.stk and self.stk[-1][0] <= price:
            del self.stk[-1]
        if self.stk:
            ans = self.idx - self.stk[-1][1]
        else:
            ans = max(ans, self.idx + 1)
        self.stk.append([price, self.idx])
        self.idx += 1
        return ans 


# Your StockSpanner object will be instantiated and called as such:
# obj = StockSpanner()
# param_1 = obj.next(price)

题目91124. 表现良好的最长时间段

解题思路:首先构建数组ss[i+1] = 1 if hours[i] > 8 else -1。然后,顺序遍历数组s,初始化递减栈stk。最后,逆序遍历数组s,得到最终答案res。注意,由于数组s表示前缀和,因此,s[stk.top()] < s[i],对应的区间为(stk.top(),i],因此长度为i - stk.top()

C++代码如下,

class Solution {
public:
    int longestWPI(vector<int>& hours) {
        //计算s
        int n = hours.size();
        vector<int> s(n+1, 0);
        for (int i = 0; i < n; ++i) {
            s[i+1] = s[i] + (hours[i] > 8 ? 1 : -1);
        }
        //初始化stk
        stack<int> stk;
        for (int i = 0; i < n+1; ++i) {
            while (stk.empty() || s[stk.top()] > s[i]) {
                stk.push(i);
            }
        }
        //计算res
        int res = 0; 
        for (int i = n; i >= 0; --i) {
            while (!stk.empty() && s[stk.top()] < s[i]) {
                res = max(res, i - stk.top()); //s表示"前缀和",故长度为i-j,对应s[j] < s[i]。
                stk.pop();
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def longestWPI(self, hours: List[int]) -> int:
        #计算数组s
        n = len(hours)
        s = [0] * (n+1) 
        for i in range(n):
            s[i+1] = s[i] + (1 if hours[i] > 8 else -1)
        #正序遍历数组s,初始化栈stk
        stk = []
        for i in range(n+1):
            while not stk or s[stk[-1]] > s[i]:
                stk.append(i)
        res = 0
        #逆序遍历数组s,计算答案res
        for i in range(n,-1,-1):
            while stk and s[stk[-1]] < s[i]:
                res = max(res, i - stk[-1])
                del stk[-1]
        return res 
            
            

题目101793. 好子数组的最大分数

解题思路:首先计算数组anums[i]左侧最近的比它小的数的下标)和数组bnums[i]右侧最近的比它小的数的下标),然后正序遍历数组nums,如果a[i] < k < b[i],则更新答案res = max(res, nums[i] * (b[i] - a[i] - 1))

C++代码如下,

class Solution {
public:
    int maximumScore(vector<int>& nums, int k) {
        int n = nums.size();
        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧最近的比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }
        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧最近的比它小的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }
        //计算答案
        int res = 0;
        for (int i = 0; i < n; ++i) {
            int left = a[i];
            int right = b[i];
            if (left < k && k < right) {
                res = max(res, nums[i] * (right - left - 1));
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maximumScore(self, nums: List[int], k: int) -> int:
        n = len(nums)
        a = [-1] * n #a[i]表示nums[i]左侧最近的小于它的元素的下标
        b = [n] * n #b[i]表示nums[i]右侧最近的小于它的元素的下标
        #计算数组a
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        #计算数组b
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i) 
        res = 0
        for i in range(n):
            left = a[i]
            right = b[i]
            if left < k < right:
                ans = nums[i] * (right - left - 1)
                res = max(res, ans)
        return res 

题目11456. 132 模式

解题思路:单调栈。如果stk.top() < nums[i],则弹出栈顶,直到条件不满足,最后把nums[i]推入栈中。

C++代码如下,

class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int n = nums.size();
        if (n < 3) {
            return false;
        }
        int second = -(1e9 + 10);
        stack<int> stk;
        for (int i = n-1; i >= 0; --i) {
            if (nums[i] < second) {
                return true;
            }
            while (!stk.empty() && stk.top() < nums[i]) {
                second = stk.top();
                stk.pop();
            }
            stk.push(nums[i]);
        }
        return false;
    }
};

python3代码如下,

class Solution:
    def find132pattern(self, nums: List[int]) -> bool:
        n = len(nums)
        if n < 3:
            return False 
        second = -inf 
        stk = []
        for i in range(n-1,-1,-1):
            if nums[i] < second:
                return True #i,j,k分别为i,stk[-1],second
            while stk and stk[-1] < nums[i]:
                second = stk[-1]
                del stk[-1]
            stk.append(nums[i])
        return False  

题目123113. 边界元素是最大值的子数组数目

解题思路:单调递减栈。

C++代码如下,

class Solution {
public:
    long long numberOfSubarrays(vector<int>& nums) {
        int n = nums.size();
        long long res = n;
        stack<pair<int,int>> stk;
        stk.push(make_pair(1e9+10,0));
        for (auto x : nums) {
            while (stk.top().first < x) {
                stk.pop();
            }
            if (stk.top().first == x) {
                res += stk.top().second;
                auto t = stk.top();
                stk.pop();
                t.second += 1;
                stk.push(t);
            } else {
                stk.push(make_pair(x,1));
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def numberOfSubarrays(self, nums: List[int]) -> int:
        n = len(nums)
        res = n 
        stk = [[inf, 0]]
        for x in nums:
            while stk[-1][0] < x:
                del stk[-1]
            if stk[-1][0] == x:
                res += stk[-1][1]
                stk[-1][1] += 1
            else:
                stk.append([x,1])
        return res 
        

题目132866. 美丽塔 II

解题思路:单调栈。单调递增。最大的累加和。

C++代码如下,

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& nums) {
        int n = nums.size();
        //计算数组a
        vector<long long> a(n, 0); //a[i]表示区间[0,i]的最大的累加和
        stack<int> stk;
        stk.push(-1);
        long long curr = 0; //当前累加和
        for (int i = 0; i < n; ++i) {
            while (stk.size() > 1 && nums[stk.top()] > nums[i]) {
                int j = stk.top();
                stk.pop();
                curr -= (1ll * j - stk.top()) * nums[j];
            }
            curr += (1ll * i - stk.top()) * nums[i];
            a[i] = curr;
            stk.push(i);
        }

        //计算数组b
        vector<long long> b(n, 0); //b[i]表示区间[i,n-1]的最大的累加和
        stk = stack<int>();
        stk.push(n);
        curr = 0; //当前累加和
        for (int i = n-1; i >= 0; --i) {
            while (stk.size() > 1 && nums[stk.top()] > nums[i]) {
                int j = stk.top();
                stk.pop();
                curr -= (1ll * stk.top() - j) * nums[j];
            }
            curr += (1ll * stk.top() - i) * nums[i];
            b[i] = curr;
            stk.push(i);
        }

        //计算答案
        long long res = 0;
        for (int i = 0; i < n; ++i) {
            long long ans = a[i] + b[i] - nums[i];
            res = max(res, ans);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maximumSumOfHeights(self, nums: List[int]) -> int:
        n = len(nums)
        #计算数组a
        a = [0] * n #a[i]表示[0,i]区间内的最大累加和
        stk = [-1]
        curr = 0 #当前累加和
        for i in range(n):
            while len(stk) > 1 and nums[stk[-1]] > nums[i]:
                j = stk[-1]
                del stk[-1]
                curr -= (j - stk[-1]) * nums[j]
            curr += (i - stk[-1]) * nums[i]
            a[i] = curr 
            stk.append(i)
        
        #计算数组b
        b = [0] * n #b[i]表示[i,n-1]区间内的最大累加和
        stk = [n]
        curr = 0 #当前累加和
        for i in range(n-1,-1,-1):
            while len(stk) > 1 and nums[stk[-1]] > nums[i]:
                j = stk[-1]
                del stk[-1]
                curr -= (stk[-1] - j) * nums[j]
            curr += (stk[-1] - i) * nums[i]
            b[i] = curr 
            stk.append(i)
        
        #计算答案
        res = 0
        for i in range(n):
            ans = a[i] + b[i] - nums[i]
            res = max(res, ans)
        return res 
        
        

题目141944. 队列中可以看到的人数

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    vector<int> canSeePersonsCount(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n, 0);
        stack<int> stk;
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] < nums[i]) {
                res[i] += 1;
                stk.pop();
            }
            if (!stk.empty()) {
                res[i] += 1;
            }
            stk.push(i);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def canSeePersonsCount(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [0] * n 
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] < nums[i]: #不会存在数值相等的情况
                res[i] += 1
                del stk[-1]
            if stk:
                res[i] += 1
            stk.append(i)
        return res 

题目152454. 下一个更大元素 IV

解题思路:两个单调递减栈。

C++代码如下,

class Solution {
public:
    vector<int> secondGreaterElement(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n, -1);
        vector<int> stk1;
        vector<int> stk2;
        for (int i = 0; i < n; ++i) {
            while (stk2.size() > 0 && nums[stk2.back()] < nums[i]) {
                int j = stk2.back();
                res[j] = nums[i]; //nums[j]的下下个更大的数是nums[i]
                stk2.pop_back();
            }
            
            int j = stk1.size() - 1;
            while (j >= 0 && nums[stk1[j]] < nums[i]) {
                j -= 1;
            }
            if (stk1.size() > 0) {
                stk2.insert(stk2.end(), stk1.begin()+j+1, stk1.end());
                stk1.erase(stk1.begin()+j+1, stk1.end());
            }
            stk1.push_back(i); 
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def secondGreaterElement(self, nums: List[int]) -> List[int]:
        n = len(nums)
        stk1 = []
        stk2 = []
        ans = [-1] * n 
        for i in range(n):
            while stk2 and nums[stk2[-1]] < nums[i]:
                j = stk2[-1]
                ans[j] = nums[i] #nums[j]的下下个更大的元素是nums[i]
                del stk2[-1]
            j = len(stk1)-1
            while j >= 0 and nums[stk1[j]] < nums[i]:
                j -= 1
            stk2 += stk1[j+1:]
            del stk1[j+1:]
            stk1.append(i)
        return ans 

题目161130. 叶值的最小代价生成树

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    int mctFromLeafValues(vector<int>& arr) {
        int n = arr.size();
        stack<int> stk;
        stk.push(20);
        int res = 0;
        for (auto x : arr) {
            while (stk.top() <= x) {
                int y = stk.top();
                stk.pop();
                res += y * min(stk.top(), x);
            }
            stk.push(x);
        }
        while (stk.size() > 2) {
            int y = stk.top();
            stk.pop();
            res += y * stk.top();
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def mctFromLeafValues(self, arr: List[int]) -> int:
        res = 0
        n = len(arr)
        stack = [inf]
        for x in arr:
            while stack[-1] <= x:
                y = stack.pop()
                res += y * min(stack[-1], x)
            stack.append(x)
        while len(stack) > 2:
            res += stack.pop() * stack[-1]
        return res 

题目172289. 使数组按非递减顺序排列

解题思路:单调递减栈。

C++代码如下,

class Solution {
public:
    int totalSteps(vector<int>& nums) {
        int res = 0;
        stack<pair<int,int>> stk;
        for (auto num : nums) {
            int ans = 0;
            while (!stk.empty() && stk.top().first <= num) {
                ans = max(ans, stk.top().second);
                stk.pop();
            }
            ans = stk.empty() ? 0 : ans + 1;
            res = max(res, ans);
            stk.push(make_pair(num, ans));
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def totalSteps(self, nums: List[int]) -> int:
        res = 0
        stk = []
        for num in nums:
            ans = 0
            while stk and stk[-1][0] <= num:
                ans = max(ans, stk[-1][1])
                del stk[-1]
            ans = ans + 1 if stk else 0
            res = max(res, ans)
            stk.append([num, ans])
        return res 

题目181776. 车队 II

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    vector<double> getCollisionTimes(vector<vector<int>>& cars) {
        int n = cars.size();
        vector<double> res(n, 0.0);
        stack<int> stk;
        for (int i = n-1; i >= 0 ;--i) {
            while (!stk.empty()) {
                if (cars[i][1] <= cars[stk.top()][1]) { //cars[i]追不上cars[stk.top()]
                    stk.pop();
                } else {
                    if (res[stk.top()] < 0) { //cars[i]能追上cars[stk.top()]
                        break;
                    } else {
                        double d = 1.0 * res[stk.top()] * (cars[i][1] - cars[stk.top()][1]);
                        if (d > cars[stk.top()][0] - cars[i][0]) { //cars[i]能追上cars[stk.top()]
                            break;
                        } else { //cars[i]追不上cars[stk.top()]
                            stk.pop();
                        }
                    }
                }
            }
            if (stk.empty()) {
                res[i] = -1; //cars[i]追不上任何车
            } else { //cars[i]能追上cars[stk.top()]
                double t = (1.0 * cars[stk.top()][0] - cars[i][0]) / (cars[i][1] - cars[stk.top()][1]);
                res[i] = t;
            }
            stk.push(i);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def getCollisionTimes(self, cars: List[List[int]]) -> List[float]:
        n = len(cars)
        res = [0] * n 
        stk = []
        for i in range(n-1,-1,-1):
            while stk:
                if cars[stk[-1]][1] >= cars[i][1]: #cars[i]不能追上cars[stk[-1]],追它前面的
                    del stk[-1] 
                else:
                    if res[stk[-1]] < 0: #cars[i]能追上cars[stk[-1]]
                        break 
                    else:
                        d = res[stk[-1]] * (cars[i][1] - cars[stk[-1]][1])
                        if d > cars[stk[-1]][0] - cars[i][0]: #cars[i]能追上cars[stk[-1]]
                            break 
                        else: #cars[i]不能追上cars[stk[-1]],追它前面的
                            del stk[-1]
            if stk: #cars[i]能追上stk[-1]
                t = (cars[stk[-1]][0] - cars[i][0]) / (cars[i][1] - cars[stk[-1]][1])
                res[i] = t 
            else: #cars[i]无车可追
                res[i] = -1
            stk.append(i)
        return res 
class Solution:
    def getCollisionTimes(self, cars: List[List[int]]) -> List[float]:
        n = len(cars)
        ans = [0] * n 
        stk = []
        for i in range(n-1,-1,-1):
            while stk:
                if cars[stk[-1]][1] >= cars[i][1]:
                    del stk[-1]
                else:
                    if ans[stk[-1]] < 0:
                        break 
                    d = ans[stk[-1]] * (cars[i][1] - cars[stk[-1]][1]) 
                    if d > cars[stk[-1]][0] - cars[i][0]:
                        break 
                    else:
                        del stk[-1]
            if not stk:
                ans[i] = -1
            else:
                t = (cars[stk[-1]][0] - cars[i][0]) / (cars[i][1] - cars[stk[-1]][1]) 
                ans[i] = t 
            stk.append(i) 
        return ans 

2.2 矩形面积

题目1984. 柱状图中最大的矩形

解题思路:两个单调栈。

C++代码如下,

class Solution {
public:
    int largestRectangleArea(vector<int>& nums) {
        int n = nums.size();
        //计算left数组
        vector<int> left(n, -1); //left[i]表示nums[i]左边首个比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                left[i] = stk.top();
            }
            stk.push(i);
        }

        //计算right数组
        vector<int> right(n, n); //right[i]表示nums[i]右边首个比它小的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                right[i] = stk.top();
            }
            stk.push(i);
        }

        //计算答案
        int res = 0;
        for (int i = 0; i < n; ++i) {
            int ans = nums[i] * (right[i] - left[i] - 1);
            res = max(res, ans);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def largestRectangleArea(self, nums: List[int]) -> int:
        n = len(nums)
        #计算left
        left = [-1] * n #left[i]表示nums[i]左边首个比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                left[i] = stk[-1]
            stk.append(i)
        
        #计算right
        right = [n] * n #right[i]表示nums[i]右边首个比它小的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                right[i] = stk[-1]
            stk.append(i)
        
        #计算答案
        res = 0
        for i in range(n):
            ans = nums[i] * (right[i] - left[i] - 1)
            #print(f"i = {i}, left[i] = {left[i]}, right[i] = {right[i]}, ans = {ans}.")
            res = max(res, ans)
        return res 

题目201793. 好子数组的最大分数

解题思路:两个单调栈。

C++代码如下,

class Solution {
public:
    int maximumScore(vector<int>& nums, int k) {
        int n = nums.size();
        //计算left数组
        vector<int> left(n, -1);
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                left[i] = stk.top();
            }
            stk.push(i);
        }

        //计算right数组
        vector<int> right(n, n);
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                right[i] = stk.top();
            }
            stk.push(i);
        }

        //计算res
        int res = 0;
        for (int i = 0; i < n; ++i) {
            if (left[i] < k && k < right[i]) {
                int ans = nums[i] * (right[i] - left[i] - 1);
                res = max(res, ans);
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def maximumScore(self, nums: List[int], k: int) -> int:
        n = len(nums)
        #计算left数组
        left = [-1] * n #left[i]表示nums[i]左侧首个比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                left[i] = stk[-1]
            stk.append(i)
        
        #计算right数组
        right = [n] * n #right[i]表示nums[i]右侧首个比它小的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                right[i] = stk[-1]
            stk.append(i)
        
        #计算res
        res = 0
        for i in range(n):
            if left[i] < k < right[i]:
                ans = nums[i] * (right[i] - left[i] - 1)
                res = max(res, ans)
        return res 

题目2185. 最大矩形

解题思路:柱状图的最大矩形面积,注意柱状图的构造。单调栈。

C++代码如下,

class Solution {
public:
    int f(const vector<int>& nums) {
        int n = nums.size();
        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧最近比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧最近比它小的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算答案
        int res = 0;
        for (int i = 0; i < n; ++i) {
            int ans = nums[i] * (b[i] - a[i] - 1);
            res = max(res, ans);
        }
        return res;
    }

    int maximalRectangle(vector<vector<char>>& matrix) {
        int n = matrix.size();
        int m = matrix[0].size();
        vector<int> nums(m, 0);
        int res = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (matrix[i][j] == '1') {
                    nums[j] += 1;
                } else {
                    nums[j] = 0;
                }
            }
            int ans = f(nums);
            res = max(res, ans);
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def f(self, nums):
        n = len(nums)
        #计算数组a
        a = [-1] * n #a[i]表示nums[i]左侧最近的比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        
        #计算数组b
        b = [n] * n #b[i]表示nums[i]右侧最近的比它小的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i)
        
        #计算答案
        res = 0
        for i in range(n):
            ans = nums[i] * (b[i] - a[i] - 1)
            res = max(res, ans)
        return res 
    
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        n, m = len(matrix), len(matrix[0]) 
        nums = [0] * m 
        res = 0
        for i in range(n):
            for j in range(m):
                if matrix[i][j] == '1':
                    nums[j] += 1
                else:
                    nums[j] = 0
            #计算柱状图nums中的最大矩形
            ans = self.f(nums)
            res = max(res, ans)
        return res 

题目221504. 统计全 1 子矩形

解题思路:单调栈。

C++代码如下,

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        int n = mat.size();
        int m = mat[0].size();
        int res = 0;
        vector<int> nums(m, 0);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (mat[i][j] == 1) {
                    nums[j] += 1;
                } else {
                    nums[j] = 0;
                }
            }
            //计算nums对答案的贡献
            int curr = 0;
            stack<int> stk;
            stk.push(-1);
            for (int j = 0; j < m; ++j) {
                while (stk.size() > 1 && nums[stk.top()] > nums[j]) {
                    int idx = stk.top();
                    stk.pop();
                    curr -= (idx - stk.top()) * nums[idx];
                }
                curr += (j - stk.top()) * nums[j];
                res += curr;
                stk.push(j);
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def numSubmat(self, mat: List[List[int]]) -> int:
        n = len(mat)
        m = len(mat[0])
        res = 0
        nums = [0] * m 
        for i in range(n):
            for j in range(m):
                if mat[i][j] == 1:
                    nums[j] += 1
                else:
                    nums[j] = 0 
            #计算nums数组对答案的贡献
            stk = [-1]
            curr = 0
            for j in range(m):
                while len(stk) > 1 and nums[stk[-1]] > nums[j]:
                    idx = stk.pop()
                    curr -= (idx - stk[-1]) * nums[idx]
                curr += (j - stk[-1]) * nums[j]
                res += curr 
                stk.append(j)
        return res 
class Solution:
    def numSubmat(self, mat: List[List[int]]) -> int:
        n = len(mat)
        m = len(mat[0])
        res = 0
        nums = [0] * m 
        for i in range(n):
            for j in range(m):
                if mat[i][j] == 1:
                    nums[j] += 1
                else:
                    nums[j] = 0
            #计算数组nums对答案的贡献
            stk = [-1]
            curr = 0
            for j in range(m):
                while len(stk) > 1 and nums[stk[-1]] > nums[j]:
                    idx = stk.pop()
                    curr -= (idx - stk[-1]) * nums[idx]
                curr += (j - stk[-1]) * nums[j]
                res += curr 
                stk.append(j)
        return res 

题目2342. 接雨水

解题思路:多种做法。

C++代码如下,

class Solution {
public:
    int trap(vector<int>& nums) {
        int n = nums.size();
        //计算数组a
        vector<int> a(n, 0); //a[i]表示区间[0,i]的最大值
        a[0] = nums[0];
        for (int i = 1; i < n; ++i) {
            a[i] = max(a[i-1], nums[i]);
        }

        //计算数组b
        vector<int> b(n, 0); //b[i]表示区间[i,n-1]的最大值
        b[n-1] = nums[n-1];
        for (int i = n-2; i >= 0; --i) {
            b[i] = max(b[i+1], nums[i]);
        }

        //计算最终答案
        int res = 0;
        for (int i = 0; i < n; ++i) {
            int x = min(a[i], b[i]);
            res += x - nums[i];
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def trap(self, nums: List[int]) -> int:
        n = len(nums)
        #计算数组a
        a = [0] * n #a[i]表示区间[0,i]的最大值
        a[0] = nums[0]
        for i in range(1,n):
            a[i] = max(a[i-1], nums[i])
        
        #计算数组b
        b = [0] * n #a[i]表示区间[i,n-1]的最大值
        b[n-1] = nums[n-1]
        for i in range(n-2,-1,-1):
            b[i] = max(b[i+1], nums[i])
        
        #计算答案
        res = 0
        for i in range(n):
            x = min(a[i], b[i])
            res += x - nums[i]
        return res 

2.3 贡献法

题目24907. 子数组的最小值之和

解题思路:两个单调栈,避免相同的数重复计算贡献。

C++代码如下,

class Solution {
public:
    int sumSubarrayMins(vector<int>& nums) {
        int n = nums.size();
        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧第1个比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧第1个比它小或等于它的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] > nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算答案
        const int mod = 1e9 + 7;
        int res = 0;
        for (int i = 0; i < n; ++i) {
            int ans = 1ll * nums[i] * (i - a[i]) * (b[i] - i) % mod;
            res += ans;
            res %= mod;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def sumSubarrayMins(self, nums: List[int]) -> int:
        n = len(nums)
        #计算数组a
        a = [-1] * n #a[i]表示nums[i]的上一个比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i) 

        #计算数组b
        b = [n] * n #b[i]表示nums[i]的下一个比它小或等于它的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] > nums[i]: 
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i)

        #计算res
        res = 0
        for i in range(n):
            ans = nums[i] * (i - a[i]) * (b[i] - i)
            res += ans 
        mod = int(1e9 + 7)
        res %= mod 
        return res 

题目252104. 子数组范围和

解题思路:分别计算最大值和最小值的贡献。单调栈。

C++代码如下,

class Solution {
public:
    long long f1(const vector<int>& nums) {
        //计算最大值贡献
        int n = nums.size();
        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧比它大的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] <= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧比它大或者等于它的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] < nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算答案
        long long res = 0;
        for (int i = 0; i < n; ++i) {
            long long curr = 1ll * nums[i] * (i - a[i]) * (b[i] - i);
            res += curr;
        }
        return res;
    }

    long long f2(const vector<int>& nums) {
        //计算最小值贡献
        int n = nums.size();
        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧比它小或等于它的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] > nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算答案
        long long res = 0;
        for (int i = 0; i < n; ++i) {
            long long ans = 1ll * nums[i] * (i - a[i]) * (b[i] - i);
            res += ans;
        }
        return res;
    }

    long long subArrayRanges(vector<int>& nums) {
        long long res = 0;
        long long ans1 = f1(nums); //计算最大值的贡献
        long long ans2 = f2(nums); //计算最小值的贡献
        res = ans1 - ans2;
        return res;
    }
};

python3代码如下,

class Solution:
    def f1(self, nums):
        #计算最大值的贡献
        res = 0
        n = len(nums)
        #计算数组a
        a = [-1] * n #a[i]表示nums[i]左侧第1个比它大的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] <= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        
        #计算数组b
        b = [n] * n #b[i]表示nums[i]右侧第1个比它大或等于它的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] < nums[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i)
        
        #计算贡献
        for i in range(n):
            res += nums[i] * (i - a[i]) * (b[i] - i)
        return res 
    
    def f2(self, nums):
        #计算最小值的贡献
        res = 0
        n = len(nums)
        #计算数组a
        a = [-1] * n #a[i]表示nums[i]左侧比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        
        #计算数组b
        b = [n] * n #b[i]表示nums[i]右侧比它小或等于它的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] > nums[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i) 
        
        #计算贡献
        for i in range(n):
            res += nums[i] * (i - a[i]) * (b[i] - i)
        return res

    def subArrayRanges(self, nums: List[int]) -> int:
        res = 0
        res += self.f1(nums) #最大值的贡献
        res -= self.f2(nums) #最小值的贡献
        return res 

题目261856. 子数组最小乘积的最大值

解题思路:单调栈。前缀和。

C++代码如下,

class Solution {
public:
    int maxSumMinProduct(vector<int>& nums) {
        int n = nums.size();
        //计算数组nums中区间[i,j]的累加和s[j+1]-s[i]
        vector<long long> s(n+1,0);
        for (int i = 0; i < n; ++i) {
            s[i+1] = s[i] + nums[i]; 
        }

        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧第1个比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧第1个比它小的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算答案
        long long res = 0;
        for (int i = 0; i < n; ++i) {
            int left = a[i];
            int right = b[i];
            //区间(left,right),区间[left+1,right-1]的累加和
            long long curr = 1ll * nums[i] * (s[right] - s[left+1]);
            res = max(res, curr);
        }
        const int mod = 1e9 + 7;
        res %= mod;
        return res;
    }
};

python3代码如下,

class Solution:
    def maxSumMinProduct(self, nums: List[int]) -> int:
        n = len(nums)
        #计算nums中[i,j]区间内的累加和,s[j+1]-s[i]
        s = [0] * (n+1) 
        for i in range(n):
            s[i+1] = s[i] + nums[i]
        
        #计算数组a
        a = [-1] * n #a[i]表示nums[i]左侧比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        
        #计算数组b
        b = [n] * n #b[i]表示nums[i]右侧比它小的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i)
        
        #计算答案
        res = 0
        for i in range(n):
            left = a[i]
            right = b[i]
            #区间(left,right),区间[left+1,right-1]的累加和s[right-1+1]-s[left+1]
            curr = nums[i] * (s[right] - s[left+1])
            res = max(res, curr)
        mod = int(1e9 + 7)
        res %= mod 
        return res 
        

题目272818. 操作使得分最大

解题思路:快速幂、单调栈、质数筛法。注意leetcode网站中的C++语言的初始化。

int init =[]() {
    //业务部分
    return 0;
}();

C++代码如下,

const int N = 1e5 + 10;
int cnt[N] = {0};
int init = []() {
    for (int i = 2; i < N; ++i) {
        if (cnt[i] == 0) {
            for (int j = i; j < N; j += i) {
                cnt[j] += 1;
            }
        }
    }
    return 0;
}();

class Solution {
public:
    long long qmi(int a, int b, int p) {
        //计算a^b%p
        long long res = 1;
        while (b) {
            if (b & 1) res = res * a % p;
            b >>= 1;
            a = 1ll * a * a % p;
        }
        return res;
    }

    int maximumScore(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> arr(n, 0);
        for (int i = 0; i < n; ++i) {
            arr[i] = cnt[nums[i]];
        }

        // for (int i = 0; i < n; ++i) {
        //     cout << arr[i] << ",";
        // }
        // cout << endl;

        //计算数组a
        vector<int> a(n, -1); //a[i]表示arr[i]左侧第1个大于或等于它的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && arr[stk.top()] < arr[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示arr[i]右侧第1个大于它的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && arr[stk.top()] <= arr[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算贡献
        vector<pair<int,long long>> data;
        for (int i = 0; i < n; ++i) {
            data.push_back(make_pair(nums[i],(1ll * i-a[i])*(b[i]-i)));
        }

        sort(data.begin(), data.end());
        reverse(data.begin(), data.end());

        const int mod = 1e9 + 7;
        long long res = 1;
        for (int i = 0; i < n; ++i) {
            int x = data[i].first;
            long long y = data[i].second;
            //x表示数值,y表示次数
            if (k >= y) {
                res = res * qmi(x, y, mod) % mod;
                k -= y;
            } else {
                res = res * qmi(x, k, mod) % mod;
                k = 0; 
            }
            if (k == 0) {
                break;
            }
        }
        return res;
    }
};

python3代码如下,

N = int(1e5 + 10)
cnt = [0] * N 
for i in range(2,N):
    if cnt[i] == 0:
        for j in range(i,N,i):
            cnt[j] += 1

class Solution:    
    def qmi(self, a, b, p):
        #计算a^b%p
        res = 1
        while b > 0:
            if b & 1:
                res = res * a % p 
            b >>= 1
            a = a * a % p 
        return res 

    
    def maximumScore(self, nums: List[int], k: int) -> int:
        #计算10^5+10以内的数的不同质因子的个数
        n = len(nums)
        #cnt = self.f()
        
        arr = [0] * n 
        for i in range(n):
            arr[i] = cnt[nums[i]]

        #计算数组a
        a = [-1] * n #a[i]表示数arr[i]左侧第1个大于或等于它的数的下标
        stk = []
        for i in range(n):
            while stk and arr[stk[-1]] < arr[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        
        #计算数组b
        b = [n] * n #b[i]表示arr[i]右侧第1个大于它的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and arr[stk[-1]] <= arr[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i)

        #计算贡献
        data = []
        for i in range(n):
            data.append([nums[i], (i-a[i])*(b[i]-i)])
        data.sort(reverse=True)
        res = 1
        mod = int(1e9 + 7)
        for x,y in data:
            #x表示数值,y表示次数
            if k >= y:
                res = res * self.qmi(x, y, mod) % mod 
                k -= y
            else:
                res = res *self.qmi(x, k, mod) % mod 
                k = 0
            if k == 0:
                break 
        return res 

题目282281. 巫师的总力量和

解题思路:以nums[i]为最小值的区间为[left,right]且要包含i,那么可以:遍历区间左端点l[left,i]l \in [left, i],遍历区间右端点r[i,right]r \in [i, right],区间[l,r]的累积和为s[r+1]-l。用公式表示如下,

l=leftir=iright(s[r+1]s[l])=l=leftir=irights[r+1]l=leftir=irights[l]=(ileft+1)r=irights[r+1](righti+1)l=leftis[l]=(ileft+1)(ss[right+2]ss[i+1])(righti+1)(ss[i+1]ss[left])\sum_{l=left}^i \sum_{r=i}^{right}(s[r+1]-s[l]) \\ =\sum_{l=left}^i\sum_{r=i}^{right} s[r+1]-\sum_{l=left}^i \sum_{r=i}^{right} s[l] \\ =(i-left+1)\sum_{r=i}^{right}s[r+1]-(right-i+1)\sum_{l=left}^is[l] \\ =(i-left+1)\cdot (ss[right+2]-ss[i+1])-(right-i+1)\cdot (ss[i+1]-ss[left])

C++版本需要注意加法、减法、乘法与模运算的等价性。

C++代码如下,

class Solution {
public:
    int totalStrength(vector<int>& nums) {
        int n = nums.size();
        const int mod = 1e9 + 7;
        //数组nums区间[i,j]的前缀和为s[j+1]-s[i]
        vector<long long> s(n+1, 0);
        for (int i = 0; i < n; ++i) {
            s[i+1] = s[i] + nums[i];
        }
        //数组s区间[i,j]的前缀和为ss[j+1]-ss[i]
        vector<long long> ss(n+2,0);
        for (int i = 0; i < n+1; ++i) {
            ss[i+1] = ss[i] + s[i];
        }

        //计算数组a
        vector<int> a(n, -1); //a[i]表示nums[i]左侧第1个比它小的数的下标
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                a[i] = stk.top();
            }
            stk.push(i);
        }

        //计算数组b
        vector<int> b(n, n); //b[i]表示nums[i]右侧第1个比它小或等于它的数的下标
        stk = stack<int>();
        for (int i = n-1; i >= 0; --i) {
            while (!stk.empty() && nums[stk.top()] > nums[i]) {
                stk.pop();
            }
            if (!stk.empty()) {
                b[i] = stk.top();
            }
            stk.push(i);
        }

        //计算贡献
        long long res = 0;
        for (int i = 0; i < n; ++i) {
            int left = a[i] + 1;
            int right = b[i] - 1;
            long long curr = (1ll*i-left+1) % mod *((ss[right+2]-ss[i+1]) % mod) - (1ll*right-i+1) % mod * ((ss[i+1]-ss[left]) % mod);
            curr %= mod;
            curr = curr * nums[i] % mod;
            res = (res + curr) % mod;
        }
        res = (res + mod) % mod;
        return res;

    }
};

python3代码如下,

class Solution:
    def totalStrength(self, nums: List[int]) -> int:
        n = len(nums)
        #计算数组nums在区间[i,j]的累加和s[j+1]-s[i]
        s1 = [0] * (n+1) #计算1次前缀和
        for i in range(n):
            s1[i+1] = s1[i] + nums[i]
        s2 = [0] * (n+2) #计算2次前缀和
        for i in range(n+1):
            s2[i+1] = s2[i] + s1[i]
        
        #计算数组a
        a = [-1] * n #a[i]表示数组nums[i]左侧比它小的数的下标
        stk = []
        for i in range(n):
            while stk and nums[stk[-1]] >= nums[i]:
                del stk[-1]
            if stk:
                a[i] = stk[-1]
            stk.append(i)
        
        #计算数组b
        b = [n] * n #b[i]表示数组nums[i]右侧比它小或等于它的数的下标
        stk = []
        for i in range(n-1,-1,-1):
            while stk and nums[stk[-1]] > nums[i]:
                del stk[-1]
            if stk:
                b[i] = stk[-1]
            stk.append(i)
        
        #计算贡献
        res = 0
        for i in range(n):
            left = a[i] + 1
            right = b[i] - 1
            #nums中的区间[left,right]
            curr = nums[i] * ((i-left+1) * (s2[right+2] - s2[i+1]) - (right-i+1)*(s2[i+1]-s2[left]))
            res += curr 
        mod = int(1e9 + 7)
        res %= mod 
        return res 

2.4 最小字典序

题目29402. 移掉 K 位数字

解题思路:字典序最小,单调递增栈。

C++代码如下,

class Solution {
public:
    string removeKdigits(string num, int k) {
        int n = num.size();
        vector<char> stk;
        int cnt = 0;
        for (auto c : num) {
            while (cnt < k && stk.size() > 0 && stk.back() > c) {
                stk.pop_back();
                cnt += 1;
            }
            stk.push_back(c);
        }
        
        if (cnt < k) {
            //还有k-cnt个字符需要删除
            for (int i = 0; i < k-cnt; ++i) {
                stk.pop_back();
            }
        }

        // cout << "cnt = " << cnt << ", k = " << k << endl;
        // for (int i = 0; i < stk.size(); ++i) {
        //     cout << "i = " << i << ", stk[i] = " << stk[i] << endl;
        // }

        //去除前导零0
        if (stk.size() > 0 && stk[0] == '0') {
            int i = 0;
            while (i < stk.size() && stk[i] == '0') {
                i += 1;
            }
            vector<char> new_stk;
            for (int j = i; j < stk.size(); ++j) {
                new_stk.push_back(stk[j]);
            }
            stk = new_stk; //更新
        }

        //构造答案
        string res = "";
        for (auto c : stk) {
            res += c;
        }

        //如果为空字符串"",则返回"0"
        if (res == "") {
            res = "0";
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        #字典序最小,单调递增栈
        n = len(num)
        stk = []
        cnt = 0
        for x in num:
            while cnt < k and stk and stk[-1] > x:
                del stk[-1]
                cnt += 1
            stk.append(x)
        if cnt < k: #还可以移除k-cnt个数
            stk = stk[0:cnt-k]
        #去掉前导零
        res = "".join(stk)
        res = res.lstrip('0')
        #如果是空字符,返回"0"
        if res == "":
            res = "0"
        return res 

题目301673. 找出最具竞争力的子序列

解题思路:字典序最小,单调递增栈。

C++代码如下,

class Solution {
public:
    vector<int> mostCompetitive(vector<int>& nums, int k) {
        int n = nums.size();
        k = n - k; //需要删除k个数
        //cout << "n = " << n << ", k = " << k << endl;
        vector<int> stk;
        int cnt = 0;
        for (auto x : nums) {
            while (cnt < k && stk.size() > 0 && stk.back() > x) {
                stk.pop_back();
                cnt += 1;
            }
            stk.push_back(x);
        }
        
        if (cnt < k) {
            //还有k-cnt个数需要删除
            for (int i = 0; i < k-cnt; ++i) {
                stk.pop_back();
            }
        }
        return stk;
    }
};

python3代码如下,

class Solution:
    def mostCompetitive(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        k = n - k #删除k个元素,使得nums中字典序最小
        stk = []
        cnt = 0
        for x in nums:
            while cnt < k and stk and stk[-1] > x:
                del stk[-1]
                cnt += 1
            stk.append(x)
        if cnt < k:
            #还剩下k-cnt个数需要删除
            stk = stk[0:cnt-k]
        return stk

题目31316. 去除重复字母

解题思路:字典序最小,则单调递增。正序遍历字符串s,如果答案中已经有s[i],则跳过。如果res[-1] > s[i] and left[res[-1]] > 0,则将字符res[-1]从答案中删除。其中left[res[-1]] > 0表示第i位右侧还有字符res[-1]

C++代码如下,

class Solution {
public:
    string removeDuplicateLetters(string s) {
        int n = s.size();
        //计算字符个数
        unordered_map<char, int> left;
        for (auto c : s) left[c] += 1;

        //计算答案
        vector<char> stk;
        set<char> has;
        for (auto c : s) {
            left[c] -= 1;
            if (has.find(c) != has.end()) { //答案中已经存在字符c了,跳过
                continue;
            }
            while (stk.size() > 0 && stk.back() > c && left[stk.back()] > 0) {
                //可以删除字符stk.back()
                has.erase(stk.back());
                stk.pop_back();
            }
            stk.push_back(c);
            has.insert(c);
        }

        //构造答案
        string res = "";
        for (auto c : stk) {
            res += c;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def removeDuplicateLetters(self, s: str) -> str:
        left = collections.Counter(s) #剩余字符的个数
        n = len(s)
        res = []
        has = set() #答案中的字符
        for c in s:
            left[c] -= 1
            if c in has: #答案中已经有字符c,跳过
                continue 
            while res and res[-1] > c and left[res[-1]]:
                has.remove(res[-1])
                del res[-1]
            res.append(c)
            has.add(c)
        return "".join(res) 

题目32Q3. 整理书架

解题思路:字典序最小,单调递增栈。

C++代码如下,

class Solution {
public:
    vector<int> arrangeBookshelf(vector<int>& order, int limit) {
        int n = order.size();
        unordered_map<int, int> left; //order中第i位右侧,该类书籍有多少个
        for (auto x : order) left[x] += 1;
        unordered_map<int, int> cnt; //当前选择的书籍中,该类书籍有多少个
        vector<int> res; //答案
        for (auto x : order) {
            left[x] -= 1;
            if (cnt[x] == limit) { //答案中已经有limit个该类书籍了,跳过
                continue;
            }
            while (res.size() > 0 && res.back() > x && (cnt[res.back()]-1 + left[res.back()]) >= limit) {
                cnt[res.back()] -= 1;
                res.pop_back();
            }
            res.push_back(x);
            cnt[x] += 1;
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def arrangeBookshelf(self, order: List[int], limit: int) -> List[int]:
        n = len(order)
        left = collections.Counter(order) #第i位右侧还有多少个该类书籍
        cnt = collections.defaultdict(int) #选择的书籍中该类书籍的个数
        res = [] #选择的书籍
        for x in order:
            left[x] -= 1
            if cnt[x] == limit: #答案中已经有limit个该类书籍
                continue 
            while res and res[-1] > x and (cnt[res[-1]]-1 + left[res[-1]] >= limit):
                cnt[res[-1]] -= 1
                del res[-1]
            res.append(x)
            cnt[x] += 1
        return res 
            

class Solution:
    def arrangeBookshelf(self, order: List[int], limit: int) -> List[int]:
        delete = collections.Counter(order) #需要删除的该类书籍的个数
        for x in delete:
            delete[x] -= limit 
        #print(f"delete = {delete}.")
        n = len(order)
        res = []
        has = collections.defaultdict(int) #答案中的该类书籍的个数
        for x in order:
            #print(f"[before]x = {x}, res = {res}.")
            if has[x] == limit: #如果该类书籍的数目已经达到limit,则跳过
                delete[x] -= 1
                continue 
            while res and res[-1] > x and delete[res[-1]] > 0:
                has[res[-1]] -= 1 #更新has
                delete[res[-1]] -= 1 #更新delete
                del res[-1]
            res.append(x)
            has[x] += 1 #更新has
        return res         

题目331081. 不同字符的最小子序列

解题思路:字典序最小,单调递增栈。

C++代码如下,

class Solution {
public:
    string smallestSubsequence(string s) {
        unordered_map<char, int> left; //字符串s中第i个字符右侧,该类字符有多少个
        for (auto c : s) left[c] += 1;
        set<char> has; //答案中的字符
        vector<char> res;
        for (auto c : s) {
            left[c] -= 1;
            if (has.find(c) != has.end()) { //答案中已经有字符c了,跳过
                continue;
            }
            while (res.size() > 0 && res.back() > c && left[res.back()] > 0) {
                has.erase(res.back());
                res.pop_back();
            }
            res.push_back(c);
            has.insert(c);
        }
        string res_s = "";
        for (auto c : res) res_s += c;
        return res_s;
    }
};

python3代码如下,

class Solution:
    def smallestSubsequence(self, s: str) -> str:
        left = collections.Counter(s) #字符串s中第i个字符右侧,该类字符的数目
        has = set()
        n = len(s)
        res = []
        for c in s:
            left[c] -= 1
            if c in has: #答案中已经有字符c了,跳过
                continue 
            while res and res[-1] > c and left[res[-1]] > 0:
                has.remove(res[-1])
                del res[-1]
            res.append(c)
            has.add(c)
        return "".join(res)

题目34321. 拼接最大数

解题思路:从nums1nums2中分别取出arr1arr2,两个子数组的长度之和为k,遍历所有的长度组合。从nums中选择长度为k的最大字典序子序列,可以使用单调递减栈实现。然后合并arr1arr2,合并时需要定义一个compare(arr1, i, arr2, j)函数。

已知,

{0an0bma+b=k{0an0kam{0ankmak\begin{cases} 0 \leq a \leq n \\ 0 \leq b \leq m \\ a + b = k \end{cases} \Rightarrow \begin{cases} 0 \leq a \leq n \\ 0 \leq k-a \leq m \end{cases} \Rightarrow \begin{cases} 0 \leq a \leq n \\ k-m \leq a \leq k \end{cases}

故,

a[max(0,km),min(n,k)]a\in [max(0,k-m),min(n,k)]

C++代码如,

class Solution {
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int n = nums1.size();
        int m = nums2.size();
        int left = max(0, k-m);
        int right = min(k, n);
        vector<int> res(k, 0);
        for (int a = left; a < right+1; ++a) {
            vector<int> arr1 = get_max_sub(nums1, a);
            vector<int> arr2 = get_max_sub(nums2, k-a);
            vector<int> curr = merge(arr1, arr2);
            if (compare(curr, 0, res, 0) > 0) {
                res = curr;
            }
        }
        return res;
    }

    vector<int> get_max_sub(const vector<int>& nums, int k) {
        //cout << "===start get_max_sub" << endl;
        //cout << "nums = ";
        //for (auto x : nums) cout << x << ",";
        //cout << endl;
        //cout << "k = " << k << endl;
        vector<int> res;
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            while (res.size() > 0 && res.back() < nums[i] && (res.size()-1 + n-i) >= k) {
                res.pop_back();
            }
            res.push_back(nums[i]);
        }
        res.erase(res.begin() + k, res.end());
        //cout << "res = ";
        //for (auto x : res) cout << x << ",";
        //cout << endl;
        //cout << "==end get_max_sub" << endl;
        return res;
    }

    vector<int> merge(const vector<int>& nums1, const vector<int>& nums2) {
        // cout << "===before merge" << endl;
        // cout << "nums1: ";
        // for (auto x : nums1) cout << x << ",";
        // cout << endl;
        // cout << "nums2: ";
        // for (auto x : nums2) cout << x << ",";
        // cout << endl;
        int n = nums1.size();
        int m = nums2.size();
        int i = 0;
        int j = 0;
        vector<int> res;
        while (i < n || j < m) {
            if (compare(nums1, i, nums2, j) > 0) {
                res.push_back(nums1[i]);
                i += 1;
            } else {
                res.push_back(nums2[j]);
                j += 1;
            }
        }
        // cout << "res: ";
        // for (auto x : res) cout << x << ",";
        // cout << endl;
        // cout << "===end merge" << endl;
        return res;
    }

    int compare(const vector<int>& nums1, int i, const vector<int>& nums2, int j) { //注意返回值类型为int
        int n = nums1.size();
        int m = nums2.size();
        while (i < n && j < m) {
            int difference = nums1[i] - nums2[j];
            if (difference != 0) {
                return difference;
            } else {
                i += 1;
                j += 1;
            }
        }
        return (n-i)-(m-j) > 0;
    }
};

python3代码如下,

class Solution:
    def maxNumber(self, nums1: List[int], nums2: List[int], k: int) -> List[int]:
        n, m = len(nums1), len(nums2)
        left = max(0, k-m)
        right = min(k, n)
        res = [0] * k #最佳答案
        for a in range(left, right+1):
            arr1 = self.get_max_sub(nums1, a)
            arr2 = self.get_max_sub(nums2, k-a)
            curr = self.merge(arr1, arr2)
            if self.compare(curr, 0, res, 0) > 0:
                res = curr 
        return res 
    
    def get_max_sub(self, nums: List[int], k: int) -> List[int]:
        res = []
        n = len(nums)
        for i,x in enumerate(nums):
            while res and res[-1] < x and (n-i+len(res)-1) >= k:
                del res[-1]
            res.append(x)
        return res[0:k]
    
    def merge(self, nums1: List[int], nums2: List[int]) -> List[int]:
        n, m = len(nums1), len(nums2)
        i = j = 0
        res = []
        while i < n or j < m:
            if self.compare(nums1, i, nums2, j) > 0:
                res.append(nums1[i])
                i += 1
            else:
                res.append(nums2[j])
                j += 1
        return res 
    
    def compare(self, nums1: List[int], i: int, nums2: List[int], j: int) -> bool:
        n, m = len(nums1), len(nums2) 
        while i < n and j < m:
            difference = nums1[i] - nums2[j]
            if difference != 0:
                return difference 
            else:
                i += 1
                j += 1
        return (n-i) - (m-j)
class Solution:
    def maxNumber(self, nums1: List[int], nums2: List[int], k: int) -> List[int]:
        m, n = len(nums1), len(nums2)
        maxSubsequence = [0] * k 
        start, end = max(0, k-n), min(k, m)

        for i in range(start, end+1):
            subsequence1 = self.getMaxSubsequence(nums1, i)
            subsequence2 = self.getMaxSubsequence(nums2, k-i)
            curMaxSubsequence = self.merge(subsequence1, subsequence2)
            if self.compare(curMaxSubsequence, 0, maxSubsequence, 0) > 0:
                maxSubsequence = curMaxSubsequence 
        return maxSubsequence 

    def getMaxSubsequence(self, nums: List[int], k: int) -> int:
        stack = [0] * k 
        top = -1
        remain = len(nums) - k 

        for i, num in enumerate(nums):
            while top >= 0 and stack[top] < num and remain > 0:
                top -= 1
                remain -= 1
            if top < k-1:
                top += 1
                stack[top] = num 
            else:
                remain -= 1
        return stack 

    def merge(self, subsequence1: List[int], subsequence2: List[int]) -> List[int]:
        x, y = len(subsequence1), len(subsequence2)
        if x == 0:
            return subsequence2 
        if y == 0:
            return subsequence1 
        
        mergeLength = x + y 
        merged = list()
        index1 = index2 = 0

        for _ in range(mergeLength):
            if self.compare(subsequence1, index1, subsequence2, index2) > 0:
                merged.append(subsequence1[index1])
                index1 += 1
            else:
                merged.append(subsequence2[index2])
                index2 += 1
        return merged 
    
    def compare(self, subsequence1: List[int], index1: int, subsequence2: List[int], index2: int) -> int:
        x, y = len(subsequence1), len(subsequence2)
        while index1 < x and index2 < y:
            difference = subsequence1[index1] - subsequence2[index2]
            if difference != 0:
                return difference 
            index1 += 1
            index2 += 1
        return (x-index1) - (y-index2) 

题目352030. 含特定字母的最小子序列

解题思路:题目要求是选择k个字符,满足字典序尽量小且字符letter的出现次数大于等于repetition。分成两步走。(1)选择至少k个字符,满足字典序尽量小且字符letter的出现次数大于等于repetition。(2)删除len(res)-k个字符,同时满足字典序尽量小且字符letter的出现次数大于等于repetition

C++代码如下,

class Solution {
public:
    string smallestSubsequence(string s, int k, char letter, int repetition) {
        int n = s.size();
        unordered_map<char, int> left; //字符串s中[i,n)区间内该字符的出现次数
        for (auto c : s) left[c] += 1;
        unordered_map<char, int> cnt; //答案中该字符的出现次数
        vector<char> res;
        for (int i = 0; i < n; ++i) {
            while (res.size() > 0 && res.back() > s[i] && res.size()-1+n-i >= k) {
                if ((res.back() != letter) || (res.back() == letter && cnt[letter]-1+left[letter] >= repetition)) {
                    cnt[res.back()] -= 1;
                    res.pop_back();
                } else {
                    break;
                }
            }
            res.push_back(s[i]);
            cnt[s[i]] += 1;
            left[s[i]] -= 1; //更新left
        }

        if (res.size()-k > 0) {
            //删除多余元素res.size()-k
            int need_delete_cnt = res.size() - k; //需要删除的元素的个数
            vector<char> new_res;
            for (int i = res.size()-1; i >= 0; --i) {
                if (res[i] == letter) {
                    if (need_delete_cnt > 0 && cnt[letter]-1 >= repetition) {
                        need_delete_cnt -= 1;
                        cnt[letter] -= 1;
                        continue; //删除res[i]
                    }
                } else {
                    if (need_delete_cnt > 0) {
                        need_delete_cnt -= 1;
                        continue; //删除res[i]
                    }
                }
                new_res.push_back(res[i]);
            }
            reverse(new_res.begin(), new_res.end());
            res = new_res; //更新res
        }

        //构造答案
        string res_s = "";
        for (auto c : res) res_s += c;
        return res_s;
    }
};

python3代码如下,

class Solution:
    def smallestSubsequence(self, s: str, k: int, letter: str, repetition: int) -> str:
        #从s中选择k个字符==>从s中选择至少k个字符
        #保证这k个字符中,字符letter的出现次数>=repetition
        #保证字典序尽量小
        n = len(s)
        res = []
        left = collections.Counter(s) #left[i]表示字符串s中[i,n)该类字符有多少个
        cnt = collections.defaultdict(int) #答案中该类字符的个数
        for i,c in enumerate(s):
            while res and res[-1] > c and (len(res)-1+n-i) >= k:
                if res[-1] != letter or (res[-1] == letter and cnt[letter]-1 + left[letter] >= repetition):
                    cnt[res[-1]] -= 1
                    del res[-1]
                else: #不能删除res[-1]
                    break 
            res.append(c)
            cnt[c] += 1
            left[c] -= 1 #更新left
        
        if len(res) > k:
            #需要删除len(res)-k个数
            need_delete_cnt = len(res) - k 
            new_res = []
            for i in range(len(res)-1,-1,-1):
                if res[i] == letter:
                    #todo
                    if cnt[letter]-1 >= repetition and need_delete_cnt > 0:
                        cnt[letter] -= 1
                        need_delete_cnt -= 1
                        continue #res[i]不加入到new_res当中
                else:
                    #todo
                    if need_delete_cnt > 0:
                        need_delete_cnt -= 1
                        continue #res[i]不加入到new_res当中

                new_res.append(res[i])
            res = new_res[::-1] #更新res
        
        #构造答案
        res_s = "".join(res)
        return res_s 

3 参考

灵神力扣题单