算法学习记录 8.9 单调栈 单调队列 KMP

编程入门 行业动态 更新时间:2024-10-22 22:51:08

算法学习记录 8.9 <a href=https://www.elefans.com/category/jswz/34/1768517.html style=单调栈 单调队列 KMP"/>

算法学习记录 8.9 单调栈 单调队列 KMP

1.单调栈

省流:找出数列中每个数左边比它小且离它最近的数

数列中的数是无序的 可以通过将递减数去除的方式将数列缩减成单调数列 便于查找左边最小值

定义数组stk来存储单调递增的数字

单调栈内 如果一个数比它右侧的数小了 那么它的左边的所有数都比他小 

题解

import java.util.Scanner;public class Main{public static void main(String[] args){Scanner sc=new Scanner(System.in);int[] stk=new int[100010];int n=sc.nextInt();int tt=0;//表示栈中的元素for(int i=0;i<n;i++){int x=sc.nextInt();//如果栈非空并且栈顶元素大于x 说明栈顶元素不符合左边比x小的条件 弹出 一直找到栈里比x小 //的第一个数while(tt!=0&&stk[tt]>=x){tt--;}//栈非空就输出栈顶元素 否则说明栈里不存在比它小的元素 输出-1if(tt!=0) System.out.print(stk[tt]+" ");else System.out.print(-1+" ");//新元素进栈stk[++tt]=x;}}
}

时间复杂度:O(n) 

2.单调队列

经典问题:滑动窗口

 定义两个数组a和q 数组a用来存放数列 数组q用来模拟队列 存放的是数列的下标

首先读取数组a的数 判断当前队头存储的下标要在滑动窗口的范围内 如果队头存储的下标超出滑动窗口范围 则窗口向右移动一格

找窗口内的最小值:队列非空 且队列中已有元素与当前数组内循环到的数对比,如果大于等于x,则弹出队列

找窗口内的最大值:队列非空 且队列中已有元素与前数组a内循到的数x对比,如果小于等于x,则弹出队列

x的下标加入队列q

判断窗口大小是否是k 如果是则输出数组a中队列q存储的下标所对应的值

题解

import java.io.*;public class Main{public static void main(String[] args)throws IOException{BufferedReader br=new BufferedReader(new InputStreamReader(System.in));PrintWriter pw=new PrintWriter(new OutputStreamWriter(System.out));int[] q=new int[1000010];int[] a=new int[1000010];int hh=0,tt=-1;String[] s=br.readLine().split(" ");int n=Integer.parseInt(s[0]);int k=Integer.parseInt(s[1]);String[] str=br.readLine().split(" ");for(int i=0;i<n;i++){a[i]=Integer.parseInt(str[i]);}for(int i=0;i<n;i++){if(hh<=tt&&q[hh]<i-k+1) hh++;while(hh<=tt&&a[q[tt]]>=a[i]) tt--;q[++tt]=i;if(i>=k-1) pw.print(a[q[hh]]+" ");}pw.println();//循环完毕之后需要对hh,tt进行初始化 再求一遍最大值hh=0;tt=-1;for(int i=0;i<n;i++){if(hh<=tt&&q[hh]<i-k+1) hh++;while(hh<=tt&&a[q[tt]]<=a[i]) tt--;q[++tt]=i;if(i>=k-1) pw.print(a[q[hh]]+" ");}pw.flush();}
}

3.KMP算法

基本概念

文本串:aabaabaaf

模式串:aabaaf

前缀:不包含尾字符 包含头字符的字符串 如:a,aa,aab,aaba,aabaa

后缀:包含尾字符 不包含头字符的字符串 如:f,af,aaf,baaf,abaaf

求模式串的最长相等前后缀(前缀表)

a  aa  aab  aaba  aabaa  aabaaf

0   1     0       1         2          0

步骤:

1.创建next数组

1.1初始化

定义 i 表示后缀末尾, j 表示前缀末尾,s表示模式串

j 初始为0,next[ 0 ]为 0

for(int i=1;i<=s.length();i++)

1.2处理前后缀不相同的情况

j > 0 且遇到冲突即前后缀不相同的情况( s[ i ] != s[ j +1] ),j 进行回退

j 回退到前一位next数组对应的位置 即j=next[j];

1.3处理前后缀相同的情况

前后缀相同的情况( s[ i ] ==s[ j + 1 ] )   j++

1.4更新next数组

next[ i ]= j ;

for(int i=2,j=0;i<=n;i++){//判断j>0且数组s的i和j+1位置上字符是否相同 不相同则j回退 回退到next数组中记录下标去while(j>0&&p[i]!=p[j+1]) j=next[j];//如果相同,j继续移动 记录最长相同前后缀长度if(p[i]==p[j]) j++;//记录每个字符的最大相同前后缀长度next[i]=j;
}

 2.匹配文本串

做法相同 不过是文本串和模式串的匹配对比

for(int i=1,j=0;i<=m;i++){//子串p和字符串s对比 如遇到不相同的字符 j回退while(j>0&&s[i]!=p[j+1]) j=next[j];if(s[i]==p[j+1]) j++;if(j==n){//输出i-n即为子串出现的起始下标//防止后面仍然存在相同子串 j进行一次回退操作j=next[j];}}

 完整代码

import java.io.*;public class Main{public static void main(String[] args)throws IOException{BufferedReader br=new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));int N=100010,M=1000010;int n=Integer.parseInt(br.readLine());String s1=br.readLine();char[] p=new char[N];for(int i=1;i<=n;i++)p[i]=s1.charAt(i-1);int m=Integer.parseInt(br.readLine());String s2=br.readLine();char[] s=new char[M];for(int i=1;i<=m;i++)s[i]=s2.charAt(i-1);int[] next=new int[N];//创建next数组for(int i=2,j=0;i<=n;i++){while(j>0&&p[i]!=p[j+1]) j=next[j];if(p[i]==p[j+1]) j++;next[i]=j;}for(int i=1,j=0;i<=m;i++){while(j>0&&s[i]!=p[j+1]) j=next[j];if(s[i]==p[j+1]) j++;if(j==n){bw.write((i-n)+" ");j=next[j];}}bw.flush();bw.close();br.close();}
}

时间复杂度:O(n+m) 

java常见的快读快写模版:

使用时主函数记得抛出IO异常(throws IOException)

1.快读每一个数字

StreamTokenizer re=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
//读入数据
re.nextToken();
int x=(int)re.nval;

StreamTokenizer 类中 有个方法是 nval 这个方法就是我们读取数据的方法,默认是double类型的,所以我们在读入的时候要将他强制转换类型。
nextToken()方法,这个方法是我们每次读入数据之前必须要写的,也就是我们读入一个数据之前就要写一个这个方法才行。

2.快读每一行 

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw=new PrintWriter(new OutputStreamWriter(System.out));
//BufferedWriter bw=new BufferedWriter(new OutputStreamReader(System.out));
//读入数据
String s=bf.readLine().split(" ");
int x=Integer.parseInt(s[0]);
//输出数据
pw.print(x);
//bw.write(x);
//刷新流
pw.flush();
//bw.flush();
//bw.close();


快输出PrintWriter输出时 记得最后加个flush()。
也可以是用BufferedReader readLine后,再split。建议使用这个。少写语句,且读一行更快。
参考:

更多推荐

算法学习记录 8.9 单调栈 单调队列 KMP

本文发布于:2024-03-10 05:01:19,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1727064.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:单调   队列   算法   KMP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!