Day9 数组模拟双向链表 数组模拟栈、队列 单调栈 滑动窗口(单调队列) KMP

数组模拟单链表其实就是链式前向星
只不过是多条链而已

双向链表的话,只需要多做几步即可
注意idx的起点
上一个单链表中起始是0,那么第k个就是k-1

现在idx起始时1,第k个就是k+1
传送门

#include<iostream>

using namespace std;
const int N=1e5+10;
int l[N],r[N],val[N],idx;

void add(int k,int x){
    val[idx]=x;
    r[idx]=r[k],l[idx]=k;
    l[r[k]]=idx,r[k]=idx++;
}

void remove(int k){
    l[r[k]]=l[k];
    r[l[k]]=r[k];
}

int main(){
    int m;
    cin>>m;
    l[1]=0;
    r[0]=1;
    idx=2;
    while(m--){
        string op;
        cin>>op;
        int k,x;
        if(op=="L"){
            scanf("%d",&x);
            add(0,x);
        }else if(op=="R"){
            scanf("%d",&x);
            add(l[1],x);
        }else if(op=="D"){
            scanf("%d",&k);
            remove(k+1);
        }else if(op=="IL"){
            scanf("%d%d",&k,&x);
            add(l[k+1],x);
        }else {
            scanf("%d%d",&k,&x);
            add(k+1,x);
        }
    }
    for(int i=r[0];i!=1;i=r[i])cout<<val[i]<<" ";
    return 0;
}

数组模拟栈只需要一个tail变量就可以模拟
传送门

#include<iostream>
using namespace std;
const int N=1e5+10;
int sta[N],tt;

void push(int x){
    sta[++tt]=x;
}

void pop(){
    tt--;
}

void ask(){
    printf("%d\n",sta[tt]);
}

int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        string op;
        int x;
        cin>>op;
        if(op=="push"){
            scanf("%d",&x);
            push(x);
        }else if(op=="pop"){
            pop();
        }else if(op=="empty"){
            if(!tt){
                printf("YES\n");
            }else {
                printf("NO\n");
            }
        }else {
            
            ask();
        }
    }
    return 0;
}

数组模拟队列

也很简单
只需要头指针hh和尾指针tt
注意队列不为空时tt>=hh
传送门

#include<iostream>

using namespace std;
const int N=1e5+10;
int q[N],tt=-1,hh;

void push(int x){
    q[++tt]=x;
}

void pop(){
    hh++;
}


int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        string op;
        cin>>op;
        int x;
        if(op=="push"){
            cin>>x;
            push(x);
        }else if(op=="pop"){
            pop();
        }else if(op=="empty"){
            if(hh>tt){
                printf("YES\n");
            }else {
                printf("NO\n");
            }
        }else {
            printf("%d\n",q[hh]);
        }
    }
    return 0;
}

单调栈

单调栈就是对于数组每个数,找到其左边最近的小于此数 的数

那么就是使得栈有单调性就可以
传送门

#include<iostream>
using namespace std;
const int N=1e5+10;
int sta[N],tt;
int main(){
    int n,x;
    scanf("%d",&n);
    
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        while(tt&&sta[tt]>=x)tt--;//清除队列中比x大的数字
        
        if(tt)printf("%d ",sta[tt]);
        else printf("-1 ");
        sta[++tt]=x;                //x入栈
    }
    return 0;
}

单调队列
注意滑动窗口的滑出情况和单调情况即可
q[hh]<i-k+1
a[q[tt]]<=a[i]这会使得队列递减
a[q[tt]]>=a[i]使得队列递增
传送门

#include<iostream>

using namespace std;
const int N=1e6+10;
int q[N],a[N];

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    
    int tt=-1,hh=0;
    for(int i=0;i<n;i++){
        if(hh<=tt&&i-k+1>q[hh])hh++;//滑动窗口左端滑出
        while(hh<=tt&&a[q[tt]]>=a[i])tt--;
        
        q[++tt]=i;
        if(i>=k-1)printf("%d ",a[q[hh]]);
    }
    
    puts("");
    
    tt=-1,hh=0;
    for(int i=0;i<n;i++){
        if(hh<=tt&&q[hh]<i-k+1)hh++;//滑动窗口左端滑出,q[hh]是队列最左的,i-k+1是滑动窗口最左的
        while(hh<=tt&&a[i]>=a[q[tt]])tt--;
        
        q[++tt]=i;
        if(i>=k-1)printf("%d ",a[q[hh]]);
    }
    
    puts("");
    return 0;
}

关于KMP
以前我写的东西,自己现在也不想看了。。。

这个过程还是很复杂的
大概就是next[i]数组存放的是以i为终点的与前缀相同的长度
然后匹配目标串时就可以通过j=ne[j]来快速移动模式串

求next数组过程和匹配的过程一样,求next数组时注意要从2开始
还有不能用next编码
i、j从1开始计算,因为当j==0时,j=ne[j]是无意义的

传送门

#include<iostream>
using namespace std;

const int N=1e5+10,M=1e6+10;
char p[N],s[M];
int n,m,ne[N];

int main(){
    cin>>n>>p+1>>m>>s+1;
    for(int i=2,j=0;i<=n;i++){
        while(j&&p[i]!=p[j+1])j=ne[j];
        if(p[i]==p[j+1])j++;
        ne[i]=j;
    }
    
    for(int i=1,j=0;i<=m;i++){
        while(j&&s[i]!=p[j+1])j=ne[j];
        if(s[i]==p[j+1])j++;
        if(j==n){
            printf("%d ",i-n);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值