基础算法板子

本人将会持续的更新一些基础算法模板,希望可以得到各位的收藏。


一 .  基础数据结构

1.单调栈

算法原理:
用单调递增栈,当该元素可以入栈的时候,栈顶元素就是它左侧第一个比它小的元素。

单调栈算法既可以应用单调递增的情况,也可以应用单调递减的情况。

例题:

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

#include <bits/stdc++.h>
using namespace std;

stack<int> t;
int main()
{
    int n;
    cin>>n;

    for (int i=1;i<=n;i++){
        int x;
        cin>>x;

        while(t.size() && t.top()>=x){
            t.pop();
        }
        if(!t.size()) cout<<-1<<" ";
        else {
            cout<<t.top()<<" ";
        }
        t.push(x);
    }


}

这里我使用的是STL的stack容器,在解决问题时,也可以使用模拟栈。

如果是才学,不太理解的话,可以看一看下面这段话。

我们可以这样理解单调栈:


既然我们必须让元素满足单调性,那么每次插入就和栈顶作比较。
如果不满足某些性质,直接弹出栈顶,直到栈为空或满足该性质插入这个元素。

2.kmp算法(模式匹配算法)

kmp是用来确定一个字符串是不是另一个字符串的子串的一种算法;

在此算法中,我们会用到 p[ ] ,  s[ ],  ne[ ] ;     其中,p是我们的匹配串,s是模板串,

next 数组则是此算法的核心思想了。

一般思路:  当我们想要匹配两个字符串时,朴素的做法是分别枚举s串和p串。

但一但数据量较大,便会无法应对,所以,我们更快的算法。

这里,我们先来介绍一下next 数组:

对next[ j ] ,是p[ 1, j ]串中前缀和后缀相同的最大长度(部分匹配值),即 p[ 1, next[ j ] ] = p[ j - next[ j ] + 1, j ]。

例如:p 为 a b c a b 

对next[ 1 ] :前缀 = 空集—————后缀 = 空集—————next[ 1 ] = 0;

对next[ 2 ] :前缀 = { a }—————后缀 = { b }—————next[ 2 ] = 0;

对next[ 3 ] :前缀 = { a , ab }—————后缀 = { c , bc}—————next[ 3 ] = 0;

对next[ 4 ] :前缀 = { a , ab , abc }—————后缀 = { a . ca , bca }—————next[ 4 ] = 1;

对next[ 5 ] :前缀 = { a , ab , abc , abca }——后缀 = { b , ab , cab , bcab}————next[ 5 ] =2;

即next [ i ] 为 可匹配前缀的最后一个字符的下标。

所以kmp算法又一下两步组成:

1.构建next 数组。

2.将p 和 s 进行匹配。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int ne[N];
int main()
{
	char p[N],s[N];
	int n,m;
	
	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;
		
	}/。构建next 数组
	
	for (int i=1,j=0;i<=m;i++){
		
		while(j && s[i]!=p[j+1]){
			j=ne[i];
		}
		if(j==n) {
			printf("%d",i-n);
			j=ne[j];
		}
	}//匹配。	
}

 s串 和 p串都是从1开始的。i 从1开始,j 从0开始,每次s[ i ] 和p[ j + 1 ]比较

s[ a , b ] = p[ 1, j ] && s[ i ] != p[ j + 1 ] 此时要移动p串(不是移动1格,而是直接移动到下次能匹配的位置)

其中1串为[ 1, next[ j ] ],3串为[ j - next[ j ] + 1 , j ]。由匹配可知 1串等于3串,3串等于2串。所以直接移动p串使1到3的位置即可。这个操作可由j = next[ j ]直接完成。 如此往复下去,当 j == m时匹配成功。

3.单调队列(滑动窗口)

这就是滑动窗口。

思路:   用队列来进行维护,始终保证队列中的元素是单调的。

既可以用stl中的队列来实现,也可以用模拟队列实现。

模拟队列:

#include <iostream>
using namespace std;

const int N = 1000010;
int a[N],q[N],hh,tt=-1;

int main()
{
    int n,k;
    cin>>n>>k;

    for (int i=0;i<n;i++){
        cin>>a[i];
        if(i-k+1>q[hh]) ++hh;//及时的弹出队首的元素。

        while(hh<=tt && a[i]<=a[q[tt]]) --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(i-k+1>q[hh]) hh++;

        while(hh<=tt && a[i]>=a[q[tt]]) --tt;
        q[++tt]=i;
        if(i+1>=k) cout<<a[q[hh]]<<" ";
    }//这是输出窗口内最小值。
}

stl deque 实现:

#include <bits/stdc++.h>
using namespace std;

const int N = 1000010;
int a[N];
deque<int> q;
int main()
{
	int n,k;
	cin>>n>>k;
	
	for (int i=1;i<=n;i++){
		cin>>a[i];
	}
	
    for (int i=1;i<=n;i++){
        while(q.size() && q.back()>a[i]) {
            q.pop_back();
        }
        
        q.push_back(a[i]);
        if(i>=k+1 && a[i-k]==q.front()){
            q.pop_front();
        } 
        
        if(i>=k) cout<<q.front()<<" ";
    }
	
	q.clear();
}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值