模板题---1.5(单调栈,单调队列,kmp,manachar)

news2024/9/28 13:26:14

这里写目录标题

  • 1.单调栈
  • 2.单调队列
  • 3.kmp算法
  • 4.manachar算法

这里记录几个基本的数据结构算法

1.单调栈

要点:

  1. 数据结构是栈
  2. 栈要维护单调性

这就是单调栈的基本定义

举个例子:

(栈底)1 3 5 7 (栈顶)

<-------这就是一个单调栈😋

当然,定义很简单,但是用的时候不一定很顺利,相反,用的时候非常的绕圈子,但是首先定个基调,这个算法非常简单。

拿几个题目作为模板题来练习:
acwing单调栈
在这里插入图片描述

在这里插入图片描述
这道题是单调栈最简单的题。
目的是:找到左边第一个小于当前的数
那么这种情况是满足的:数组从左到右严格递增
这样就可以保证每个数左边第一个小于自己的数被快速找到,因为答案就是左边第一个数。

比如:

1 2 3 4 5

1,2,3,4,5左边第一个小于自己的数就是他们本身左边第一个数。

最后,目的就是去构建并且维护这样一个单调的数组。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N = 1e5 + 10;

int a[N];
int b[N];
stack<int>st;
int n;


int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
    {
        while (!st.empty() && st.top() >= a[i])  //如果栈不空,且栈顶元素大于等于当前元素,将栈顶弹出(维护栈单调性)
               st.pop();
        if (st.empty())   //如果栈空,说明a[i]左边没有比它小的元素
            b[i] = -1;
        else             //否则有,记录
            b[i] = st.top();    
        st.push(a[i]);    
    }

    for (int i = 1; i <= n; i++)
        cout << b[i] << " ";
    return 0;
}

也可以不需要数组b,用一些小技巧:

-----00

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N = 1e5 + 10;

int a[N];
stack<int>st;
int n;


int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
    {
        while (!st.empty() && st.top() >= a[i])  //如果栈不空,且栈顶元素大于等于当前元素,将栈顶弹出(维护栈单调性)
               st.pop();
        int t=a[i];
        if(st.empty())
            a[i]=-1;
        else
            a[i]=st.top();
        st.push(t);
            
    }

    for (int i = 1; i <= n; i++)
        cout << a[i] << " ";
    return 0;
}

继续练习,这个题掌握了,单调栈基本上就一知半解了😋

洛谷里面有几个题:
1.P1901 发射站
在这里插入图片描述
这个题目翻译一下:
分别找到比第i个数左边第一个比第i个数高的数和右边第一个高的数。

那么分为两个问题
1.找到第i个数左边第一个比它高的数
2.找到第i个数右边第一个比它高的数

那么这样的序列可以一次找到左边第一个比它高的数:

5 4 3 2 1

那么这样的序列可以一次找到右边第一个比他高的数:

1 2 3 4 5

如果这样来拆分,实际上就是套用了两次模板。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;

const int N = 1e6 + 10;

struct node
{
	int h, v,i;
}A[N];

int energy[N][2];//energy[i][0]表示左边第一个比i高的位置下标,energy[i][1]表示右边第一个比i高的下标
int ans[N];  
stack<node>st;

int n;

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int a, b;
		cin >> a >> b;
		A[i].h = a, A[i].v = b, A[i].i = i;
	}

	//分别找出比当前数左边第一个高的和右边第一个高的
	//左边第一个高
	for (int i = 1; i <= n; i++)  //只要维护的是一个单调递减数列,那么就是合理的
	{
		while (!st.empty() && st.top().h<= A[i].h)st.pop();  //如果当前能量塔高度大于等于栈顶,栈顶元素弹出
		if (st.empty())
			energy[i][0] = 0;
		else
			energy[i][0] = st.top().i;
		st.push(A[i]);
	}
	//先清空栈重复利用
	while(!st.empty())
		st.pop();
	//右边第一个高,实际上就是反过来找左边第一高,
	for (int i = n; i >= 1; i--)
	{
		while (!st.empty() && st.top().h <= A[i].h)st.pop();  //如果当前能量塔高度大于等于栈顶,栈顶元素弹出
		if (st.empty())
			energy[i][1] = 0;
		else
			energy[i][1] = st.top().i;
		st.push(A[i]);
	}

	int Max = -99999;
	
	for (int i = 1; i <= n; i++)   //由于每个能量塔都会传播能量到左右,所以用一个数组ans记录所有接收的能量
	{
		ans[energy[i][0]] += A[i].v;
		ans[energy[i][1]] += A[i].v;
	}
	for (int i = 1; i <= n; i++)  //最后找到接受能量最多的
		Max = max(Max, ans[i]);
	cout << Max << endl;
	return 0;
}

2.单调队列

和单调栈大同小异,维护队列的单调性
滑动窗口

#include<iostream>
#include<cstring>
using namespace std;

const int N=1e6+10;

int a[N];   ///模拟数组
int q[N];
int tt,hh;

int n,k;

int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)
        cin>>a[i];
    
    hh=0,tt=-1;  //hh是队头(左),tt是队尾(右)
    for(int i=0;i<n;i++)  //维护单调递增队列
    {
        if(hh<=tt&&q[hh]<i-k+1)hh++;   //如果窗口左边下标大于队头下标,那么队头弹出,hh右移一个单位
        while(hh<=tt&&a[q[tt]]>=a[i])tt--;  //维护单调递增队列
        q[++tt]=i;
        if(i>=k-1)
            cout<<a[q[hh]]<<" ";
    }
    cout<<endl;
    
    hh=0,tt=-1;  //hh是队头(左),tt是队尾(右)
    for(int i=0;i<n;i++)  //维护单调递增队列
    {
        if(hh<=tt&&q[hh]<i-k+1)hh++;   //如果窗口左边下标大于队头下标,那么队头弹出,hh右移一个单位
        while(hh<=tt&&a[q[tt]]<=a[i])tt--;  //维护单调递增队列
        q[++tt]=i;
        if(i>=k-1)
            cout<<a[q[hh]]<<" ";
    }
    cout<<endl;
    return 0;
}

3.kmp算法

首先定一个基调,这是一个异常烦人的算法,和那个manachar算法一样折磨人。
这题注意,字符下标要从1开始,否则ne[j]会出现j==-1的清空,数组不允许下标是-1
题目:KMP模板
学习链接,任何人都可以看懂:董晓老师算法

#include<iostream>
using namespace std;
const int N=1e6+10;
char S[N],P[N];
int ne[N];
int n,m;


int main()
{
    cin>>n;
    scanf("%s",P+1);
    cin>>m;
    scanf("%s",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)
        {
            cout<<i-n<<" ";
            j=ne[j];
        }
    }
    return 0;
}

4.manachar算法

这个问题的来源是解决这样一个问题:给定一个字符串S,找出这个字符串里面的最长回文子串。
比如abbaba ,其中最长的回文子串是abba,所谓回文串,就是正着反着读是一样的。而子串一定要保证连续的。

题目:最长回文子串

这个题和kmp扩展算法基本一样。

class Solution {
public:
    string longestPalindrome(string s) 
    {
        //第一步构造新字符串
        string ss="";
        ss+="@#";
        for(int i=0;i<s.length();i++)
        {
            ss+=s[i];
            ss+="#";
        }
        //构造一个数组d[N]记录所有字符的回文半径 
        //初始化1,d[i]=1表示以i为中心的字符串的回文半径为1
        vector<int>d(ss.length(),1); 
        //开始manachar算法
        for(int i=1,boxl=0,boxr=0,x=0;i<ss.length();i++)
        {
            int y=2*x-i;   //y是元素ss[i]关于x的对称点
            if(boxr>=i)d[i]=min(d[y],boxr-i+1);
            while(ss[i+d[i]]==ss[i-d[i]])d[i]++;
            if(boxr<i+d[i]-1)   //当前盒子的右端点大于老盒子的右端点
            {
                x=i;
                boxl=i-d[i]+1;
                boxr=i+d[i]-1;
            }
        }

        int a,b;  //记录最长回文串的下标和回文半径
        for(int i=1;i<ss.length();i++)
        {
            if(d[i]>a)
            {
                a=d[i];
                b=i;
            }
        }
        string ans="";
        for(int i=b-a+1;i<=b+a-1;i++)
        {
            if(ss[i]!='#')
                ans+=ss[i];
        }
        return ans;

    }
};

不用leetcode的话这样写

#include<iostream>
#include<cstring>
using namespace std;

const int N = 1e7 + 10;

int d[N];
string s, ss,ans;

string manachar(string &s)
{
	ss = ans = "";
	ss += "$#";
	for (int i = 0; i < s.length(); i++)
	{
		ss += s[i];
		ss += "#";
	}
	for (int i = 0; i < ss.length(); i++)
		d[i] = 1;
	for (int i = 1, l = 0, r = 0, x = 0; i < ss.length(); i++)
	{
		int y = 2 * x - i;
		if (r >= i)d[i] = min(d[y], r - i + 1);
		while (ss[i + d[i]] == ss[i - d[i]])d[i]++;
		if (r < i + d[i] - 1)
		{
			x = i;
			l = i - d[i] + 1;
			r = i + d[i] - 1;
		}
	}
	int index=0, Maxlen=-1;
	for (int i = 1; i < ss.length(); i++)
	{
		if (Maxlen < d[i])
		{
			Maxlen = d[i];
			index = i;
		}
	}

	for (int i = index - Maxlen + 1; i <= index + Maxlen - 1; i++)
	{
		if (ss[i] != '#')
			ans += ss[i];
	}
	puts(" ");
	return ans;
}
int main()
{
	cout << "输入字符串:";
	cin >> s;
	cout << "最长回文子串是:" << endl;
	cout << manachar(s) << endl;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行为型模式 - 备忘录模式Memento

学习而来&#xff0c;代码是自己敲的。也有些自己的理解在里边&#xff0c;有问题希望大家指出。 模式的定义与特点 在备忘录模式&#xff08;Memento Pattern&#xff09;下&#xff0c;为的是在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象…

【C++ Primer】阅读笔记(2):const | constexpr | 类型别名 |auto

目录 简介const指针顶层const与底层const常量表达式constexpr类型别名auto参考结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过…

CTF中的PHP特性函数(下)

前言 上篇文章讲的进阶一些的PHP特性不知道大家吸收的怎么样了&#xff0c;今天作为本PHP特性函数的最后一篇&#xff0c;我也会重点介绍一些有趣的PHP特性以及利用方法&#xff0c;下面开始我们今天的内容分享。 parse_str parse_str()这个函数会把查询字符串解析到变量中。…

二叉树——链式存储

✅<1>主页&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;数据结构——二叉树 &#x1f525;<3>创作者&#xff1a;我的代码爱吃辣 ☂️<4>开发环境&#xff1a;Visual Studio 2022 &#x1f4ac;<5>前言&#xff1a;上期讲了…

LinkedList与链表(一)(非循环单向链表)

ArrayList的缺陷通过ArrayList上节课的学习&#xff0c;我们了解到如果ArrayList要删除或插入一个元素&#xff0c;后面的元素都要进行移动&#xff0c;时间复杂度为O(n),效率比较低&#xff0c;因此ArrayList不适合做任意位置的插入和删除操作比较多的场景。因此java集合又引入…

Python if __name__ == “__main__“ 用法

文章目录1 前言2 原理3 __name__变量的作用参考1 前言 在很多Python程序中&#xff0c;我们都会遇到if __name__ "__main__"的情况&#xff0c;却不知道为何要这样做 在很多编程语言中&#xff0c;如C、Java等&#xff0c;都需要程序的入口&#xff0c;一般都是ma…

MySql锁机制(全网最全、最详细、最清晰)

1、MySql锁机制 锁机制的作用&#xff1a; 解决因为资源共享&#xff0c;而造成的并发问题。 没有锁机制时&#xff1a; 例如一号用户和二号用户都要去买同一件商品&#xff08;假如这件商品是一件衣服&#xff09;&#xff0c;一号用户手速稍微快了一些&#xff0c;于是就…

从事软件测试需要学自动化么

相信许多对软件测试有过一点了解的人&#xff0c;对自动化都不会感到陌生。我们常常会听到一定软件测试人员一定要学自动化的说法&#xff0c;那么很多人都会有这样的疑问&#xff0c;从事软件测试为什么要学自动化&#xff1f;事实上&#xff0c;如今只会功能测试的从业者往往…

光波导成为AR眼镜迭代新趋势,二维扩瞳几何光波导潜力彰显

关注AR眼镜的朋友可能都会发现&#xff0c;近期新品迭代的一个趋势是持续在小型化、轻量化方向演进。与一年前光学方案主要以BirdBath不同的是&#xff0c;消费级AR眼镜正快速向光波导方案探索和转变。这一点在最近发布的众多新品AR眼镜中就能明显的感受到&#xff0c;以视享G5…

堆排序 TopK 优先级队列的部分源码 JAVA对象的比较

一.堆排序:我们该如何借助堆来对数组的内容来进行排序呢&#xff1f; 假设我们现在有一个数组&#xff0c;要求从小到大进行排序&#xff0c;我们是需要进行建立一个大堆还是建立一个小堆呢&#xff1f; 1)我的第一步的思路就是建立一个小堆&#xff0c;因为每一次堆顶上面的元…

MGRE和ospf的综合运用

目录实验需求知识点实验过程一&#xff0c;在R1 R2 R3中的MGRE搭建二&#xff0c;在R1 R4 R5中的MGRE搭建三&#xff0c;整个内网的ospf协议实验需求 一 题目要求 1&#xff0c;R6为ISP代表运营商&#xff0c;只能配置ip地址&#xff0c;R1-R5环回 代表私有网段 2&#xff0c…

【Linux】顶级编辑器Vim的基本使用及配置

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux 文章目录一、前言二、vim基本概念三、vim 基本操作1、模式切换2、命令模式3、插入模式4、底行模式四…

[Vue的数据绑定]一.Vue的数据绑定;二.Vue的事件绑定;三.Class和Style的绑定;四.Vue的过滤器;五.Vue脚手架的使用

目录 一.Vue的数据绑定 1.单向数据绑定&#xff1a;将Model绑定到View上&#xff0c;当通过JavaScript代码改变了Model时&#xff0c;View就会自动刷新。不需要进行额外的DOM操作就可以实现视图和模型的联动 &#xff08;1&#xff09;插值表达式&#xff1a;{{ 表达式 }}&am…

pandas 使用

import pandas as pd a [3,7,2] myvar pd.Series(a,index["x","y","z"]) print(myvar["x"]) #结果是3 #给序列赋值index &#xff0c;然后可以根据index找到对应的数 data {"col1": range(-5,0), "col2": r…

半入耳式耳机适合跑步吗、最适合跑步的五款耳机分享

好的跑步耳机可以帮助您跑得更努力、更快和更久。研究发现&#xff0c;听音乐可以提高跑步成绩&#xff0c;尤其是在速度方面。同时&#xff0c;随着音乐锻炼可以提高跑步者的速度并减少精神疲劳&#xff0c;帮助您自信地打破这些束缚。那么目前市面上有哪些无线耳机是适合跑步…

【算法】分治算法(第三章习题解答)

3 分治算法 3.1 设 AAA 是 nnn 个非 000 实数构成的数组, 设计一个算法重新排列数组中的数, 使得负数都排在正数前面. 要求算法使用 O(n)O(n)O(n) 的时间和 O(1)O(1)O(1) 的空间. 算法设计&#xff1a;由于算法要求使用 O(n)O(n)O(n) 的时间和 O(1)O(1)O(1) 的空间&#xff0…

力扣sql入门篇(八)

力扣sql入门篇(八) 1 订单最多的客户 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 SELECT customer_number FROM Orders GROUP BY customer_number having count(order_number)(SELECT max(o1.number)FROM (SELECT count(order_number) numberFRO…

电压放大器在超声导波声弹特性的液压管路压力检测中的应用

实验名称&#xff1a;电压放大器在超声导波声弹特性的液压管路压力检测中的应用 研究方向&#xff1a;超声检测 实验目的&#xff1a; 为实现农机装备液压系统的非介入式压力检测&#xff0c;在分析适合压力检测的超声导波声弹敏感模态与激励频率的基础上&#xff0c;搭建了一套…

用埃式筛法来求解素数。————C++

目录埃式筛法埃式筛法求解某一个数字包含的所有素数数组Code运行结果埃式筛法判断某一个数字是否为素数Code运行结果埃式筛法 首先要了解什么式埃式筛法之前&#xff0c;需要知道一个定理。 就是素数的整数倍一定不是素数。 了解了这个就基本大概懂了埃式筛法。 首先初始化…

C语言线性表实现:顺序表

文章目录&#xff1a;概念理解&#xff1a;1. 动态顺序表结构体&#xff1a;2. 顺序表动态初始化&#xff1a;3. 顺序表扩容&#xff1a;4. 插入&#xff1a;5. 删除&#xff1a;6. 按位序查找&#xff1a;7. 按值查找&#xff1a;8. 输出顺序表&#xff1a;9. 判断顺序表是否相…