本人将会持续的更新一些基础算法模板,希望可以得到各位的收藏。
一 . 基础数据结构
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();
}