刷题:单调栈与字符串
2023.11.26 cf上的1886C
解题思路
从第一个字符开始往后,删除第一对第一个字符大于第二个字符的相邻字符组中的第一个字符。还没找到就一直入栈,当即将入栈元素和栈顶元素满足上述条件时,栈顶元素出栈,继续判断,直到待入元素满足入栈条件。时间复杂度为O(n),暴力做法为O(n²)(每一次有元素出栈,要执行一次查询位置减字符串长度,字符串长度减一)
注意:由于string下标从0开始,因此输入的pos要减1
代码
#include<iostream>
#include<string>
#include<vector>
using namespace std;
#define ll long long
vector<char> st;//用vector模拟栈,stack不支持下标访问
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--)
{
st.clear();
string s;
ll pos;
cin>>s>>pos;
int len=s.length();//长度不用计入$符号
pos--;
s+='$';//在给定字符串的末尾添加小于 a 的字符,就不必考虑没有一对相邻字符满足题意的情况
bool k=0;
if(pos<len)k=1;
for(auto c:s)
{
while(!st.empty()&&!k&&c<st.back())
{
pos-=len;
len--;
st.pop_back();
if(pos<len)k=1;
}
st.push_back(c);
}
cout<<st[pos];
}
return 0;
}
单调栈
单调栈就是维护一个单调递增或者单调递减的栈,将时间复杂度降为O(n)
栈空或栈顶元素更优时元素入栈(老人仍然比新人强)
else 栈顶元素出栈(新人比老人年轻还比老人强)
单调栈一般用于解决以下几种问题:
-
寻找左侧第一个比当前元素大的元素。
-
寻找左侧第一个比当前元素小的元素。
-
寻找右侧第一个比当前元素大的元素。
-
寻找右侧第一个比当前元素小的元素。
- 数据存在单调性的问题
- 确定区间最值
- 以该元素为最值的最长区间
1.寻找左侧第一个比当前元素大的元素
从左到右遍历元素,构造单调递增栈(从栈顶到栈底递增):
一个元素左侧第一个比它大的元素就是将其「插入单调递增栈」时的栈顶元素。
如果插入时的栈为空,则说明左侧不存在比当前元素大的元素。
2.寻找左侧第一个比当前元素小的元素
从左到右遍历元素,构造单调递减栈(从栈顶到栈底递减):
一个元素左侧第一个比它小的元素就是将其「插入单调递减栈」时的栈顶元素。
如果插入时的栈为空,则说明左侧不存在比当前元素小的元素。
3.寻找右侧第一个比当前元素大的元素
从左到右遍历元素,构造单调递增栈(从栈顶到栈底递增):
一个元素右侧第一个比它大的元素就是将其「弹出单调递增栈」时即将插入的元素。
如果该元素没有被弹出栈,则说明右侧不存在比当前元素大的元素。
从右到左遍历元素,构造单调递增栈(从栈顶到栈底递增):
一个元素右侧第一个比它大的元素就是将其「插入单调递增栈」时的栈顶元素。
如果插入时的栈为空,则说明右侧不存在比当前元素大的元素。
4.寻找右侧第一个比当前元素小的元素
从左到右遍历元素,构造单调递减栈(从栈顶到栈底递减):
一个元素右侧第一个比它小的元素就是将其「弹出单调递减栈」时即将插入的元素。
如果该元素没有被弹出栈,则说明右侧不存在比当前元素小的元素。
从右到左遍历元素,构造单调递减栈(从栈顶到栈底递减):
一个元素右侧第一个比它小的元素就是将其「插入单调递减栈」时的栈顶元素。
如果插入时的栈为空,则说明右侧不存在比当前元素小的元素。
上边的分类解法有点绕口,可以简单记为以下条规则:
无论哪种题型,都建议从左到右遍历元素。
查找 「比当前元素大的元素」 就用 单调递增栈,查找 「比当前元素小的元素」 就用 单调递减栈。
从 「左侧」 查找就看 「插入栈」 时的栈顶元素,从 「右侧」 查找就看 「弹出栈」 时即将插入的元素。
ps:判断时先判empty再判st.top(),防止栈空还使用st.top()导致运行错误

浙公网安备 33010602011771号