3.29 acwing算法基础课 数据结构 算术栈 队列 单调队列(滑动窗口) 单调栈 kmp(较难)

一、

3302. 表达式求值

给定一个表达式,其中运算符仅包含 +,-,*,/(加 减 乘 整除),可能包含括号,请你求出表达式的最终值。

注意:

  • 数据保证给定的表达式合法。
  • 题目保证符号 - 只作为减号出现,不会作为负号出现,例如,-1+2,(2+2)*(-(1+1)+2) 之类表达式均不会出现。
  • 题目保证表达式中所有数字均为正整数。
  • 题目保证表达式在中间计算过程以及结果中,均不超过 231−1231−1。
  • 题目中的整除是指向 00 取整,也就是说对于大于 00 的结果向下取整,例如 5/3=15/3=1,对于小于 00 的结果向上取整,例如 5/(1−4)=−15/(1−4)=−1。
  • C++和Java中的整除默认是向零取整;Python中的整除//默认向下取整,因此Python的eval()函数中的整除也是向下取整,在本题中不能直接使用。

输入格式

共一行,为给定表达式。

输出格式

共一行,为表达式的结果。

数据范围

表达式的长度不超过 105105。

输入样例:

(2+2)*(1+1)

输出样例:

8

 思路:

1.遇到右括号则将括号中的数全部求和到左括号,求和的书进入stknum栈即可

2.注意可能多位数,遇到一个数字则使用一个while函数遍历到最后一个数字

3.当新的符号优先级低于栈顶元素,则栈顶元素出栈两个,符号栈出一个,让他们进行求和

4.unordered_map用法,类似字典,方便查询

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
#define N 100010
int stknum[N],ttnum=-1,ttfh=-1;
char stkfh[N];
unordered_map<char, int> h{ {'+', 1}, {'-', 1}, {'*',2}, {'/', 2} };
void Pushnum(int x)
{
    stknum[++ttnum]=x;
}
void Pushfh(char x)
{
    stkfh[++ttfh]=x;
}
int Popnum()
{

    return stknum[ttnum--];
}
char Popfh()
{
    return stkfh[ttfh--];
}
int seetopnum()
{
    return stknum[ttnum];
}
int seetopfh()
{
    return stkfh[ttfh];
}
bool Emptyfh()
{
    return ttfh==-1?true:false;
}
void eval()//求值
{
    int a = Popnum();//第二个操作数
    int b = Popnum();//第一个操作数
    char p = Popfh();//运算符
    int r = 0;//结果
    //计算结果
    if (p == '+') r = b + a;
    if (p == '-') r = b - a;
    if (p == '*') r = b * a;
    if (p == '/') r = b / a;
    Pushnum(r);//结果入栈
}
int main()
{
    string a;
    cin>>a;
    for(int i=0;i<a.size();i++)
    {
        if (isdigit(a[i]))//数字入栈
        {
            int x = 0, j = i;//计算数字
            while (j < a.size() && isdigit(a[j]))
            {
                x = x * 10 + a[j] - '0';
                j++;
            }
            Pushnum(x);//数字入栈
            i = j - 1;
        }
        else
        {
            if(a[i]=='(')Pushfh(a[i]);
            else if(a[i]==')')
            {
                while(seetopfh()!='(')
                        eval();
                Popfh();
            }
            else
            {
                while (!Emptyfh() && h[seetopfh()] >= h[a[i]])//待入栈运算符优先级低,则先计算
                    eval();
                Pushfh(a[i]);//操作符入栈
            }
        }
    }
    while (!Emptyfh()) eval();//剩余的进行计算
    cout << seetopnum() << endl;//输出结果
    return 0;
}

---------------------------------------------------------------------------------------------------------------------------------二、

829. 模拟队列

实现一个队列,队列初始为空,支持四种操作:

  1. push x – 向队尾插入一个数 xx;
  2. pop – 从队头弹出一个数;
  3. empty – 判断队列是否为空;
  4. query – 查询队头元素。

现在要对队列进行 MM 个操作,其中的每个操作 33 和操作 44 都要输出相应的结果。

输入格式

第一行包含整数 MM,表示操作次数。

接下来 MM 行,每行包含一个操作命令,操作命令为 push xpopemptyquery 中的一种。

输出格式

对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。

其中,empty 操作的查询结果为 YES 或 NOquery 操作的查询结果为一个整数,表示队头元素的值。

数据范围

1≤M≤1000001≤M≤100000,
1≤x≤1091≤x≤109,
所有操作保证合法。

输入样例:

10
push 6
empty
query
pop
empty
push 3
push 4
pop
query
push 6

输出样例:

NO
6
YES
4

思路:略

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
#define N 100010
int line[N],r=0,l=0;
void Push(int x)
{
    line[l++]=x;
}
int Pop()
{
    return line[r++];
}
bool Empty()
{
    return l==r?true:false;
}
int query()
{
    return line[r];
}
int main()
{
    int m;
    cin>>m;
    while(m--)
    {
        string a;
        cin>>a;
        int x;
        if(a=="pop")
        {
            Pop();
        }
        else if(a=="empty")
        {
            if(!Empty())cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
        }
        else if(a=="push")
        {
            cin>>x;
            Push(x);
        }
        else
        {
            cout<<query()<<endl;
        }
    }
    return 0;
}

 --------------------------------------------------------------------------------------------------------------------------------

三、

830. 单调栈

给定一个长度为 NN 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1−1。

输入格式

第一行包含整数 NN,表示数列长度。

第二行包含 NN 个整数,表示整数数列。

输出格式

共一行,包含 NN 个整数,其中第 ii 个数表示第 ii 个数的左边第一个比它小的数,如果不存在则输出 −1−1。

数据范围

1≤N≤1051≤N≤105
1≤数列中元素≤1091≤数列中元素≤109

输入样例:

5
3 4 2 7 5

输出样例:

-1 3 -1 2 2

思路:注意tt等于0时,则认为没有找到比t小的数;使栈中的数始终单调递增,分析三个点,若为10,15,12,则注意到15完全可以舍弃,因为在12之后的所有书,比12小可以舍去12,比12大也不可能会找到15,所以在12进入时,15比12大就可以舍去了。

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
#define N 100010
int stk[N],tt=0;
void Push(int x)
{
    stk[++tt]=x;
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int t;
        cin>>t;
        while(tt>0&&stk[tt]>=t)tt--;
        if(tt==0)cout<<"-1"<<" ";
        else cout<<stk[tt]<<" ";
        Push(t);
    }
    return 0;
}

 --------------------------------------------------------------------------------------------------------------------------------

四、

154. 滑动窗口

给定一个大小为 n≤106n≤106 的数组。

有一个大小为 kk 的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 kk 个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7],kk 为 33。

窗口位置最小值最大值
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 nn 和 kk,分别代表数组长度和滑动窗口的长度。

第二行有 nn 个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式

输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:

8 3
1 3 -1 -3 5 3 6 7

输出样例:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

思路:

//维持滑动窗口的大小
//当队列不为空(hh <= tt) 且 当当前滑动窗口的大小(i - q[hh] + 1)>我们设定的
//滑动窗口的大小(k),队列弹出队列头元素以维持滑动窗口的大小

 if(hh<=tt&&i+1-q[hh]>k)hh++;

//构造单调递增队列
//当队列不为空(hh <= tt) 且 当队列队尾元素>=当前元素(a[i])时,那么队尾元素
//就一定不是当前窗口最小值,删去队尾元素,加入当前元素(q[ ++ tt] = i)
while(hh<=tt&&a[q[tt]]>=a[i])tt--;

//q[N]存的是数组下标

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
#define N 100010
int a[N],q[N],q0[N];
int main()
{
    int n,k;
    cin>>n>>k;
    int hh=0,tt=-1;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&i+1-q[hh]>k)hh++;
        while(hh<=tt&&a[q[tt]]>=a[i])tt--;
        q[++tt]=i;
        if(i+1>=k)cout<<a[q[hh]]<<" ";
    }
    cout<<endl;
    hh=0,tt=-1;
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&i+1-q0[hh]>k)hh++;
        while(hh<=tt&&a[q0[tt]]<=a[i])tt--;
        q0[++tt]=i;
        if(i+1>=k)cout<<a[q0[hh]]<<" ";
    }
    return 0;
}

 --------------------------------------------------------------------------------------------------------------------------------

五、

831. KMP字符串

给定一个模式串 SS,以及一个模板串 PP,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模板串 PP 在模式串 SS 中多次作为子串出现。

求出模板串 PP 在模式串 SS 中所有出现的位置的起始下标。

输入格式

第一行输入整数 NN,表示字符串 PP 的长度。

第二行输入字符串 PP。

第三行输入整数 MM,表示字符串 SS 的长度。

第四行输入字符串 SS。

输出格式

共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。

数据范围

1≤N≤1051≤N≤105
1≤M≤1061≤M≤106

输入样例:

3
aba
5
ababa

输出样例:

0 2

思路:写在下面了

#include<iostream>
using namespace std;
#include<vector>
#include<bits/stdc++.h>
#define N 100010
int n,m;
char p[N],s[N];
int ne[N];

int main()
{
    //p+1和s+1的意思均为从1开始
    cin>>n>>p+1>>m>>s+1;
    //i从2开始,应为next[1]=0
    for(int i=2,j=0;i<=n;i++)
    {
        //如果j的值不为0(即不为p[0]时)且(上一次匹配成功下标为j)当前匹配p[i]
        //不等于上一次匹配成功的j下标后一个字符时,再使j向更前一次next[j]寻找
        //直到j==0或者相等为止
        while(j&&p[i]!=p[j+1])j=ne[j];
        //此时已经知道该位数next值,如果j+1位等于j位时,因为匹配成功,所以j++,表明该位next值
        if(p[i]==p[j+1])
            j++;
        ne[i]=j;
    }

    for(int i=1,j=0;i<=m;i++)
    {
        //同理,当s[i]!=p[j+1]时,得找到该数位能和s[i]匹配的,找不到就返回1,即没有相同前后缀
        while(j&&s[i]!=p[j+1])j=ne[j];
        //如果相等匹配长度+1,如果匹配长度等于短字符数组长度,则匹配成功,返回开始匹配成功的下标
        if(s[i]==p[j+1])j++;
        if(j==n)
        {
            cout<<i-n<<" ";
            j=ne[j];
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值