题目链接
//逆向统计
string decodeAtIndex(string s, int k) {
long size = 0;
int n = s.size();
for (int i = 0; i < n; ++i) { //统计解码后字符串长度
if (isdigit(s[i]))
size *= s[i] - '0';
else
size++;
}
for (int i = n - 1; i >= 0; i--) {
k %= size;
if (k == 0 && isalpha(s[i])) // k >= size 时才有可能 k % size == 0
return (string)"" + s[i];
if (isdigit(s[i]))
size /= s[i] - '0'; //eg: leet2code3: leetleetcode leetleetcode leetleetcode 除后进入组
else
size--;
}
return "";
}
//Time O(n) Space O(1)
题目链接
class FreqStack {
public:
unordered_map<int, int> num_freq; // 值对应的频率
unordered_map<int, vector<int>> freq_lists; // 同频率的列表
int max_cnt = -1;
FreqStack() {}
void push(int val) {
num_freq[val] += 1;
freq_lists[num_freq[val]].emplace_back(val);
max_cnt = max(num_freq[val], max_cnt);
}
int pop() {
int num = freq_lists[max_cnt].back();
int cnt = num_freq[num];
num_freq[num] -= 1;
freq_lists[cnt].pop_back();
if (freq_lists[cnt].empty())
--max_cnt;
return num;
}
};
//Time O(1) Space O(n)
题目链接
/* 单调栈 */
class StockSpanner {
public:
stack<pair<int, int>> monoSt; //
StockSpanner() {}
int next(int price) {
pair<int, int> ret(price, 1);
while (!monoSt.empty() && monoSt.top().first <= price) {
auto tmp = monoSt.top();
ret.second += tmp.second;
monoSt.pop();
}
monoSt.push(ret);
return ret.second;
}
};
/*
* StockSpanner* obj = new StockSpanner();
* int param_1 = obj->next(price);
*/
题目链接
/* 单调栈 */
//最小值是在一段连续数字钟被筛选出来的,也就是说每个最小值都有一定
int sumSubarrayMins(vector<int> &arr) {
int n = arr.size();
vector<int> left(n), right(n); //每个元素辐射范围的左右边界
stack<int> st;
for (int i = 0; i < n; i++) {
//向左寻找第一个小于等于 E 的元素 (小于“等于”为了解决重复相同最小值元素)
while (!st.empty() && arr[i] < arr[st.top()])
st.pop();
if (st.empty())
left[i] = -1;
else
left[i] = st.top();
st.push(i);
}
st = stack<int> ();
for (int i = n - 1; i >= 0; i--) {
//向右寻找第一个小于 E 的元素
while (!st.empty() && arr[i] <= arr[st.top()])
st.pop();
if (st.empty())
right[i] = n;
else
right[i] = st.top();
st.push(i);
}
//计算贡献值
//左右边界实际上记录的是 左边界-1 和 右边界+1
long ans = 0;
for (int i = 0; i < n; i++)
ans = (ans + (long)(i - left[i]) * (right[i] - i) * arr[i]) % 1000000007;
return (int)ans;
}
//Time O(n) Space O(n)
题目链接
/* 平衡法 */
//计算每个前缀数组的平衡度,负值代表需要加 '(',正值代表需要加 ')'
int minAddToMakeValid(string s) {
int ans = 0, bal = 0;
for (int i = 0; i < s.size(); i++) {
bal += s.[i] == '(' ? : 1 : -1;
if (bal == -1) {
ans++;
bal++; //保持 bal >= -1
}
}
return ans + bal;
}
题目链接
/* 栈模拟 */
bool validateStackSequences(vector<int> & pushed, vector<int> &popped) {
stack<int> st;
int n = pushed.size();
for (int i = 0, j = 0; i < n; i++) {
st.emplace(pushed[i]);
while (!st.empty() && st.top() == popped[j]) {
st.pop();
j++;
}
}
return st.empty();
}
题目链接
/* 单调栈 */
int maxWidthRamp(vector<int> &nums) {
int n = nums.length();
int maxWidth = 0;
stack<int> st;
//正序遍历存一个以 A[0] 开始的递减序列进栈,作为坡底
for (int i = 0; i < n; i++) {
if (st.empty() || nums[st.top()] > nums[i])
st.push(i);
}
//倒序遍历用坡顶去匹配坡底
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && nums[st.top()] <= nums[i]) {
int pos = st.top();
st.pop();
maxWidth = fmax(maxWidth, i - pos);
}
}
return maxWidth;
}
//Time O(n) Space O(n)
题目链接
bool isValid(string s) {
stack<char> st;
for (int i = 0; i < s.size(); i++) {
if (s[i] == 'a' || s[i] == 'b')
st.push(s[i]);
else { //模拟祖玛
if (st.size() < 2) return false;
if (st.top() != 'b') return false;
st.pop();
if (st.top() != 'a') return false;
st.pop();
}
}
return st.empty();
}
题目链接
int clumsy(int n) {
stack<int> st;
st.push(n);
n--;
int index = 0;
while (n > 0) { //栈中存计算结果
if (index % 4 == 0)
st.top() *= n;
else if (index % 4 == 1)
st.top() /= n;
else if (index % 4 == 2)
st.push(n);
eles
st.push(-n);
index++;
n--;
}
int sum = 0;
while (!st.empty()) {
sum += st.top();
st.pop();
}
return sum;
}
//Time O(n) Space O(n)
/* 数学 */
int clumsy(int n) {
if (n == 1)
return 1;
else if (n == 2)
return 2;
else if (n == 3)
return 6;
else if (n == 4)
return 7;
if (n % 4 == 0) {
return n + 1;
else if (n % 4 <= 2)
return n + 2;
else
return n - 1;
}
//Time O(1) Space O(1)
题目链接
/* 单调栈 */
vector<int> nextLargeNodes(ListNode *head) {
int count = 0;
vector<int> res;
stack<pair<int, int>> monoSt; //
while (head) {
res.push_back(0);
while (!monoSt.empty() && monoSt.top().first < head->val) {
res[monoSt.top().second] = head->val;
monoSt.pop(); //修改一个弹一个
}
monoSt.push(make_pair(head->val, count++)); //count++ 而不是 ++count,即先执行 m_p 再 count++
head = head->next;
}
return res;
}
题目链接
//栈从空到下一次空的过程就是扫描了一个原语的过程
string removeOuterParentheses(string s) {
string res;
stack<char> st;
for (auto c : s) {
if (c == ')') st.pop(); //先做pop动作保证最外层 '(' 被弹出而不进入 res
if (!st.empty()) res.push_back(c); //栈非空才进行记录
if (c == '(') st.emplace(c);
}
return res;
}
string removeOuterParentheses(string s) {
int level = 0;
string res;
for (auto c : s) {
if (c == ')') level--;
if (level) res.push_back(c);
if (c == '(') level++;
}
return res;
}
//Time O(n) Space O(n)
题目链接
sring removeDuplicates(string s) {
string st;
for (char ch : s) {
if (!st.empty() && st.back() == ch) st.pop_back();
else st.push_back(ch);
}
return st;
}
题目链接
/* 单调栈 + 贪心 same as 316*/
string smallestSubsequence(string s) {
vector<int> vis(26), cnt(26);
for (char ch : s)
cnt[ch - 'a']++;
string st;
for (char ch : s) {
if (!vis[ch - 'a']) {
while (!st.empty() && st.back() > ch) {
if (cnt[st.back() - 'a'] > 0) {
vis[st.back() - 'a'] = 0;
st.pop_back();
} else
break;
}
vis[ch - 'a'] = 1;
st.push_back(ch);
}
cnt[ch - 'a']--;
}
return st;
}
题目链接
bool parseBoolExpr(string expression) {
stack<char> syb, val;
for (int i = 0; i < expression.size(); i++) {
if (expression[i] == '|' || expression[i] == '&' || expression[i] == '!')
syb.push(expression[i]);
else if (expression[i] == ')') {
int t = 0, f = 0;
while (val.top() != '(') { // 't' 'f' ',' 字符都被弹出直到遇到 (
if (val.top() == 't') t++;
if (val.top() == 'f') f++;
val.pop();
}
val.pop(); // '(' 弹出
char x = syb.top();
syb.pop();
if (x == '!') { //非运算
if (t == 1) val.push('f');
else val.push('f');
}
else if (x == '|') { //或运算
if (t != 0) val.push('t');
else val.push('f');
}
else if (x == '&') { //与运算
if (f != 0) val.push('f');
else val.push('t');
}
}
else
val.push(expression[i]);
}
if (val.top() == 't')
return true;
else
return false;
}
题目链接
vector<int> maxDepthAfterSplit(string seq) {
int d = 0;
vector<int> ans;
for (char &c : seq) {
if (c == '(') {
++d;
ans.push_back(d % 2);
} else {
ans.push_back(d % 2);
--d;
}
}
return ans;
}
题目链接
/* 前缀和 + 单调栈 */
//找到一个区间 [i, j]满足:prefix[j] - prefix[i] > 0 且 max(j - i),类似找出前缀和数组 prefix 中的最长的上坡
int longestWPI(vector<int> &hours) {
int n = hours.size();
vector<int> prefix(n + 1, 0); //前缀和,prefix 开头增加一个哨兵
for (int i = 1; i <= n; i++)
prefix[i] = prefix[i - 1] + (hours[i - 1] > 8 ? 1 : -1); //统计前缀和
stack<int> st;
for (int i = 0; i <= n; i++) {
if (st.empty() || prefix[st.top()] > prefix[i]) //以 prefix[0] 开始的递减序列进栈
st.push(i);
}
int ans = 0;
for (int i = n; i >= 0; i--) { //倒序遍历用坡顶匹配坡底
while (!st.empty() && prefix[i] > prefix[st.top()]) {
ans = fmax(ans, i - st.top());
st.pop();
}
}
return ans;
}
题目链接
string reverseParentheses(string s) {
stack<string> stk;
string str;
for (auto &ch : s) {
if (ch == '(') {
stk.push(str);
str = "";
} else if (ch == ')') {
reverse(str.begin(), str.end());
str = stk.top() + str;
stk.pop();
} else {
str.push_back(ch);
}
}
return str;
}
//Time O(n^2) Space O(n)
//栈的最大深度为 n , 每一层处理(翻转)的时间复杂度为 n 。
//预处理括号
//以括号索引向导前后往复遍历字符
string reverseParentheses(string s) {
int n = s.length();
vector<int> pair(n);
stack<int> stk;
for (int i = 0; i < n; i++) {
if (s[i] == '(') {
stk.push(i);
} else if (s[i] == ')') {
int j = stk.top();
stk.pop();
pair[i] = j, pair[j] = i; //对应括号索引成组,提供检索时的入口
}
}
string ret;
int index = 0, step = 1;
while (index < n) {
if (s[index] == ')' || s[index] == ')') {
index = pair[index];
step = -step; //step -1 向前遍历入栈, +1 向后遍历入栈
} else {
ret.push_back(s[index]);
}
index += step;
}
return ret;
}
//Time O(n) Space O(n)
题目链接
/* 记忆计数 */
string removeDuplicates(string s, int k) {
vector<int> count(s.size());
for (int i = 0; i < s.size(); i++) {
if (i == 0 || s[i] != s[i - 1]) {
count[i] = 1;
} else {
count[i] = count[i - 1] + 1;
if (count[i] == k) {
s.erase(i - k + 1, k);
i = i - k;
}
}
}
return s;
}
//Time O(n) Space O(n)
string removeDuplicates(string s, int k) {
stack<int> counts; //栈内如果一直保持压入 1 说明没有重复
for (int i = 0; i < s.size(); i++) {
if (i == 0 || s[i] != s[i - 1]) {
counts.push(1);
} else if (++counts.top() == k) {
counts.pop();
s.erase(i - k + 1, k);
i = i - k;
}
}
return s;
}
//Time O(n) Space O(n)
题目链接
string minRemoveToMakeValid(string s) {
stack<int> braket;
int n = s.size();
for (int i = 0; i < n; i++) {
if (s[i] == '(') {
braket.push(i);
} else if (s[i] == ')') {
if (!braket.empty()) {
braket.pop();
} else {
s.erase(i, 1);
n--;
i--;
}
}
}
while (!braket.empty()) { //删除多余 '('
int i = braket.top();
s.erase(i, 1);
braket.pop();
}
return s;
}
题目链接
class CustomStack {
public:
vector<int> stk;
int top;
CustomStack(int maxSize) {
stk.resize(maxSize);
top = -1;
}
void push(int x) {
if (top != stk.size() - 1) {
++top;
stk[top] = x;
}
}
int pop() {
if (top == -1) return -1;
--top;
return stk[top + 1];
}
void increment(int k, int val) {
int lim = min(k, top + 1);
for (int i = 0; i < lim; i++)
stk[i] += val;
}
};
题目链接
vector<string> buildArray(vector<int> &target, int n) {
int i = 1;
vector<string> res;
for (int val : target) {
while (val != i) {
res.push_back("Push");
res.push_back("Pop");
i++;
}
res.push_back("Push");
i++;
}
return res;
}
题目链接
/* 双指针直接遍历 */
vector<int> finalPrices(vector<int> &prices) {
int n = prices.size();
vector<int> ans;
for (int i = 0; i < n; i++) {
int discount = 0;
for (int j = i + 1; j < n; j++) {
if (prices[j] <= prices[i]) {
discount = prices[j];
break;
}
}
ans.emplace_back(prices[i] - discount);
}
return ans;
}
//Time O(n^2) Space O(1)
/* 单调栈 */
vector<int> finalPrices(vector<int> &prices) {
int n = prices.size();
vector<int> ans(n);
stack<int> monoSt; //单调递增栈
for (int i = n - 1; i >= 0; i--) {
while (!monoSt.empty() && monoSt.top() > prices[i]) {
monoSt.pop();
}
ans[i] = monoSt.empty() ? prices[i] : prices[i] - monoSt.top();
monoSt.push(prices[i]);
}
return ans;
}
题目链接
/* 枚举 */
//枚举矩阵每个位置 (i, j) 作为右下角时有多少符合要求的子矩阵
//row 代表矩阵中 (i, j) 向左延伸连续的 1 的个数。有 row 数组后,枚举子矩阵高 k
int bumSubmat(vector<vector<int>> &mat) {
int n = mat.size(), m = mat[0].size();
vector<vector<int>> row(n, vector<int> (m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (j == 0) {
row[i][j] = mat[i][j];
} else if (mat[i][j]) {
row[i][j] = row[i][j - 1] + 1;
} else {
row[i][j] = 0;
}
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int col = row[i][j];
for (int k = i; k >= 0 && col; k--) {
col = min(col, row[k][j]);
ans += col;
}
}
}
return ans;
}
//Time O(n^2 * m) Space O(nm)
/* 单调栈 */
int bumSubmat(vector<vector<int>> &mat) {
int n = mat.size(), m = mat[0].size();
vector<vector<int>> row(n, vector<int> (m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (j == 0) {
row[i][j] = mat[i][j];
} else if (mat[i][j]) {
row[i][j] = row[i][j - 1] + 1;
} else {
row[i][j] = 0;
}
}
}
int ans = 0;
for (int j = 0; j < m; ++j) { //纵向由列开始统计
stack<pair<int, int>> monost; // < row[i][j], height >
int sum = 0;
for (int i = 0; i < n; i++) {
int height = 1;
while (!monost.empty() && monost.top().first > row[i][j]) {
sum -= monost.top().second * (monost.top().first - row[i][j]); //弹出时减去多于答案
height += monost.top().second;
monost.pop();
}
sum += row[i][j];
ans += sum;
monost.push({row[i][j], height});
}
}
return ans;
}
//Time O(mn) Space O(?)
题目链接
int minInsertion(string s) {
int ans = 0;
int left = 0;
int n = s.size();
int index = 0;
while (index < n) {
char c = s[index];
if (c == '(') { //char '('
left++;
index++;
} else { //char ')'
if (left > 0) {
left--;
} else {
ans++;
}
if (index < n - 1 && s[index + 1] == ')') {
index += 2; //char '))'
} else {
ans++;
index++;
}
}
}
ans += left * 2;
return ans;
}
题目链接
string makeGood(string s) {
string ret;
for (char ch : s) {
if (!ret.empty() && tolower(ret.back()) == tolower(ch) && ret.back() != ch)
ret.pop_back();
else
ret.push_back(ch);
}
return ret;
}
题目链接
/* 单调栈 */
//将数组分成左中右三段,左段和右段是非递减的。
//一个坡顶匹配一群坡底
int findLengthOfShortestSubarray(vector<int>& arr) {
stack<int> stk;
for (int i = arr.size() - 1; i >= 0; i--) {
if (stk.empty() || arr[stk.top()] >= arr[i]) {
stk.push(i);
continue;
}
break; //仅找到右段单调部分就停止
}
if (stk.size() == arr.size()) return 0;
int ans = arr.size() - stk.size(); //相对最坏情况(直接删掉中段和左段)
int prev = -1;
for (int i = 0; i < arr.size(); i++) {
if (arr[i] >= prev) { //保证左段单调
while (!stk.empty() && arr[i] > arr[stk.top()])
stk.pop();
int pos = stk.empty() ? arr.size() : stk.top(); //pos 右段坡顶
ans = min(ans, pos - i - 1);
prev = arr[i];
continue;
}
break;
}
return ans;
}
//Time O(n) Space O(n)
//同样也可以用一个坡底匹配一群坡顶,区别在于先正序遍历栈中存左段单调区间
/* 双指针优化栈 */
int findLengthOfShortestSubarray(vector<int> & arr) {
int n = arr.size();
int pos = n - 1;
while (pos > 0 && arr[pos] >= arr[pos - 1])
pos--;
if (pos == 0) return 0;
int ans = pos; //直接删掉左段和右段
int prev = -1;
for (int i = 0; i < arr.size(); i++) {
if (arr[i] >= prev) {
while (pos != n && arr[i] > arr[pos])
pos++;
ans = min(ans, pos - i - 1);
prev = arr[i];
continue; //跳过当前循环的代码强迫开始下一次循环
}
break; //循环立即终止,程序流将继续执行紧接循环的下一条语句
}
return ans;
}
//Time O(n) Space O(1)
题目链接
int minOperations(vector<string>& logs) {
int depth = 0;
for (auto & log : logs) {
if (log == "./") {
continue;
} else if (log == "../") {
if(depth > 0)
depth--;
} else {
depth++;
}
}
return depth;
}
题目链接
int maxDepth(string s) {
int ans = 0, size = 0;
for (char ch : s) {
if (ch == '(') {
++size;
ans = max(ans,size);
} else if (ch == ')') {
--size;
}
}
return ans;
}
题目链接
int minimumDeletions(string s) {
int ans = 0;
stack<char> stk;
for (char ch : s) {
if (ch == 'b') {
stk.push(ch);
} else {
if (!stk.empty()) {
ans++; //不论是删除前面的 b 还是删除当前的 a, 都增加一次删除操作计数
stk.pop();
}
}
}
return ans;
}
/* 指针优化 */
// {if ch = b; b++} {if ch = a && b > 0; ans++, b--}
/* 动态规划 */
int minimumDeletions(string s) {
int b = 0;
vector<int> dp(n + 1, 0);
for (char ch : s) {
if (ch == 'b') { //字符为 b,只要前 [0, i -1] 是平衡的 [0, i] 就是平衡的,故状态保持
dp[i + 1] = dp[i];
b++;
} else { //字符为 a,要么删除前面多余的 b, 要么删除这个
dp[i + 1] = min(dp[i] + 1, b);
}
}
return dp[n];
}
/* 滚动数组优化 */
// int ans = 0; ans = min(ans + 1, b);
题目链接
/* 单调栈 + 贪心*/
//类似 leetcode.402
vector<int> mostCompetitive(vector<int> &nums, int k) {
vector<int> stk;
int cnt = nums.size() - k; //待删除的个数
for (int & digit : nums) {
while (!stk.empty() && stk.top() > digit && cnt) {
stk.pop_back();
cnt--;
}
stk.push_back(digit);
} //维护一个单调递增的栈
for (; cnt > 0; cnt--) {
stk.pop_back();
} //栈内元素过多,从尾部开始弹
return stk;
}
题目链接
int countStudents(vector<int>& students, vector<int>& sandwiches) {
int dp[2];
int cnt = students.size();
for (int i = 0; i < students.size(); i++) {
dp[students[i]]++;
}
for (int i = 0; i < sandwiches.size(); i++) {
if (dp[sandwiches[i]] > 0) {
dp[sandwiches[i]]--;
cnt--;
} else
break;
}
return cnt;
}
题目链接
/* 贪心 */
//优先处理得分高的字符串,然后处理栈中剩余的低分子串
int maximumGain(string s, int x, int y) { //ab 得 x 分, ba 得 y 分
stack<char> ba, ab;
int ret = 0;
if (x > y) { //预处理,统一默认 ba 得 y 为高分
swap(x, y);
reverse(s.begin(), s.end());
}
for (char c : s) { //ba 高分
if (c != 'a') { //栈中弹出顺序实际为 ba 故先判断 a
ba.push(c);
} else { //新扫描到的字符为 a
if (!ba.empty() && ba.top() == 'b') {
ba.pop(); //组成 ba
ret += y;
} else {
ba.push(c);
}
}
}
while (!ba.empty()) { //处理 ab
char c = ba.top();
if (c != 'a') {
ab.push(c);
} else {
if (!ab.empty() && ab.top() == 'b') {
ab.pop();
ret += x;
} else {
ab.push(c);
}
}
ba.pop();
}
return ret;
}
题目链接
vector<double> getCollisionTime(vector<vector<int>>& cars) {
vector<double> ans(cars.size());
stack<int> st; //单调增栈,栈顶序号代表的车的车速最快,栈底最慢
for (int i = cars.size() - 1; i >= 0; i--) {
while (!st.empty()) {
if (cars[st.top()][1] >= cars[i][1]) {
st.pop(); //栈顶车速太快了永远追不上,弹掉
} else {
if (ans[st.top()] < 0) break; //如果栈顶不会消失,则跳出循环执行计算相遇时间
double dist = ans[st.top()] * (cars[i][1] - cars[st.top()][1]; //如果会消失,计算消失前能否追上
if (dist > cars[st.top()][0] - cars[i][0]) break; //若能,则跳出循环执行计算相遇时间
else st.pop(); //若不能,则弹出
}
if (st.empty()) {
ans[i] = -1;
} else {
//相遇时间点 = 相对距离 / 相对速度
double time = double(cars[st.top()][0] - cars[i][0]) / double(cars[i][1] - cars[st.top()][1]);
ans[i] = time;
}
题目链接
/* 单调栈 */
//类似 84. 柱状图最大矩形面积
int maximumScore(vector<int>& nums, int k) {
int n = nums.size();
vector<int> left(n), right(n);
//当前数字左侧最近的第一个比该数字较小元素
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] >= nums[i])
st.pop();
left[i] = st.empty() ? -1 : st.top();
st.push(i);
}
//当前数字右侧最近的第一个比该数字较小元素
st = stack<int> ();
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && nums[st.top()] >= nums[i])
st.pop();
right[i] = st.empty() ? n : st.top();
st.push(i);
}
int ans = 0;
for (int i = 0; i < n; i++) {
if (left[i] < k && right[i] > k)
ans = max(ans, (right[i] - left[i] - 1) * nums[i]);
}
return ans;
}