【算法】kmp、Trie、并查集、堆

news2024/11/13 15:08:14

文章目录

    • 1.kmp
    • 2.Trie
    • 3.并查集
    • 4.堆

1.kmp

KMP 的精髓就是 next 数组:也就是用 next[j] = k;简单理解就是:来保存子串某个位置匹配失败后,回退的位置。

给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模式串 P 在字符串 S 中多次作为子串出现。

求出模式串 P 在字符串 S 中所有出现的位置的起始下标。

输入格式

第一行输入整数 N,表示字符串 P 的长度。

第二行输入字符串 P。

第三行输入整数 M,表示字符串 S 的长度。

第四行输入字符串 S。

输出格式

共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。

数据范围

1≤N≤1051≤N≤105
1≤M≤1061≤M≤106

输入样例:

3
aba
5
ababa

输出样例:

0 2
#include <iostream>
using namespace std;

const int N = 100010,M = 1000010;
int n,m;
int ne[N];
char p[N],s[M];

int main()
{
    cin>>n>>p+1>>m>>s+1;
    
    //ne数组
    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;
    }
    
    
    //KMP匹配
    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);
            //匹配成功之后要回退
            j = ne[j];
        }
    }
    
    return 0;
}

2.Trie

Trie树是高效快速存储和查找字符串集合的数据结构。我们直接通过一幅图来了解即可:

image-20230101162134536

Trie树中有个二维数组 son[N][26],只包含小写字母,cnt数组表示当前这个点结尾的单词有多少个。下标是0的点既是根节点又是空节点

维护一个字符串集合,支持两种操作:

  1. I x 向集合中插入一个字符串 xx;
  2. Q x 询问一个字符串在集合中出现了多少次。

共有 N 个操作,所有输入的字符串总长度不超过 10^5,字符串仅包含小写英文字母。

输入格式

第一行包含整数 N,表示操作数。

接下来 N 行,每行包含一个操作指令,指令为 I xQ x 中的一种。

输出格式

对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x 在集合中出现的次数。

每个结果占一行。

数据范围

1≤N≤2∗1041≤N≤2∗104

输入样例:

5
I abc
Q abc
Q ab
I ab
Q ab

输出样例:

1
0
1
#include <iostream>
using namespace std;

const int N = 100010;
int son[N][26],cnt[N],idx;
char str[N];

void insert(char str[])
{
    int p = 0;
    for(int i = 0;str[i];i++)
    {
        int u = str[i] - 'a';
        if(!son[p][u]) son[p][u] = ++idx;
        p = son[p][u];
    }
    cnt[p]++;
}

int query(char str[])
{
    int p = 0;
    for(int i = 0;str[i];i++)
    {
        int u = str[i] - 'a';
        if(!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        char op[2];
        scanf("%s%s",op,str);
        if(op[0]=='I') insert(str);
        else printf("%d\n",query(str));
    }
    return 0;
}

3.并查集

快速处理问题:1.将两个集合合并2.询问两个元素是否在一个集合当中

基本原理:用树的形式维护集合,每个集合的编号是根结点的编号,每个结点都存储父节点是谁,p[x]表示x的父节点,所以可以快速找到每个点属于哪个集合。问题1:如何去判断树根:if(p[x]==x)

问题2:如何求x的集合编号:while(p[x]!=x) x = p[x]

问题3:合并两个集合:px是x的集合编号,py是y的集合编号,p[x]=y即可
优化问题2:路径压缩:在找根结点的路径上,所有在路径上的结点都指向根结点。可以基本看成O(1)复杂度

一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。

现在要进行 m 个操作,操作共有两种:

  1. M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;

输入格式

第一行输入整数 n 和 m。

接下来 m 行,每行包含一个操作指令,指令为 M a bQ a b 中的一种。

输出格式

对于每个询问指令 Q a b,都要输出一个结果,如果 aa 和 bb 在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1≤n,m≤1051≤n,m≤105

输入样例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

输出样例:

Yes
No
Yes
#include <iostream>
using namespace std;

int n,m;
const int N = 100010;

int p[N];


int find(int x)
{
    if(p[x]!= x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i =1;i<=n;i++) p[i] = i;
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",op,&a,&b);
        if(op[0]=='M') p[find(a)] = find(b);
        else
        {
            if(find(a) == find(b)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

题目稍微变形一下:

给定一个包含 n 个点(编号为 1∼n1∼n)的无向图,初始时图中没有边。

现在要进行 m 个操作,操作共有三种:

  1. C a b,在点 a 和点 b 之间连一条边,aa 和 bb 可能相等;
  2. Q1 a b,询问点 a 和点 b 是否在同一个连通块中,a 和 b 可能相等;
  3. Q2 a,询问点 a 所在连通块中点的数量;

输入格式

第一行输入整数 n 和 m。

接下来 m 行,每行包含一个操作指令,指令为 C a bQ1 a bQ2 a 中的一种。

输出格式

对于每个询问指令 Q1 a b,如果 aa 和 bb 在同一个连通块中,则输出 Yes,否则输出 No

对于每个询问指令 Q2 a,输出一个整数表示点 aa 所在连通块中点的数量

每个结果占一行。

数据范围

1≤n,m≤1051≤n,m≤105

输入样例:

5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

输出样例:

Yes
2
3
#include <iostream>
using namespace std;


int n,m;
const int N = 100010;
int p[N],sz[N];

int find(int x)
{
    if(p[x]!= x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=n;i++)
    {
        p[i] = i;
        sz[i] = 1;
    } 
    while(m--)
    {
        char op[5];
        int a,b;
        scanf("%s",op);
        if(op[0] == 'C')
        {
            scanf("%d%d",&a,&b);
            if(find(a) == find(b)) continue;
            sz[find(b)] += sz[find(a)];
            p[find(a)] = find(b);
        }
        else if(op[1] == '1')
        {
            scanf("%d%d",&a,&b);
            if(find(a)==find(b)) puts("Yes");
            else puts("No");
        }
        else
        {
            scanf("%d",&a);
            printf("%d\n",sz[find(a)]);
        }
    }
    return 0;
}

4.堆

分为大根堆和小根堆。小根堆是每个点都小于等于左右孩子,根结点就是最小的。根为x,则x的左孩子:2x,右孩子:2x+1。核心操作是down和up,向下和向上调整。对于小根堆,down的逻辑:如果某个值变大了,往下移。up的逻辑:某个值变小了,往上移。

image-20230102152227092

堆排序

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。

输入格式

第一行包含整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

输出格式

共一行,包含 m 个整数,表示整数数列中前 mm 小的数。

数据范围

1≤m≤n≤1051≤m≤n≤105,
1≤数列中元素≤1091≤数列中元素≤109

输入样例:

5 3
4 5 1 3 2

输出样例:

1 2 3
#include <iostream>
using namespace std;

const int N = 100010;
int n,m;
int heap[N],sz;

void down(int u)
{
    int t = u;
    if(u*2<=sz&&heap[u*2]<heap[t]) t = u*2;
    if(u*2+1<=sz&&heap[u*2+1]<heap[t]) t = u*2+1;
    if(u!=t)
    {
        swap(heap[u],heap[t]);
        down(t);
    }
}

void up(int u)
{
    while(u/2&&heap[u/2]>heap[u])
    {
        swap(heap[u/2],heap[u]);
        u/=2;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=n;i++) scanf("%d",&heap[i]);
    sz=n;
    for(int i = n/2;i;i--) down(i);
    while(m--)
    {
        printf("%d ",heap[1]);
        heap[1] = heap[sz];
        sz--;
        down(1);
    }
    return 0;
}

模拟堆

维护一个集合,初始时集合为空,支持如下几种操作:

  1. I x,插入一个数 x;
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 k 个插入的数;
  5. C k x,修改第 k 个插入的数,将其变为 x;

现在要进行 N 次操作,对于所有第 2 个操作,输出当前集合的最小值。

输入格式

第一行包含整数 N。

接下来 N 行,每行包含一个操作指令,操作指令为 I xPMDMD kC k x 中的一种。

输出格式

对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。

每个结果占一行。

数据范围

1≤N≤1051≤N≤105
−109≤x≤109−109≤x≤109
数据保证合法。

输入样例:

8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM

输出样例:

-10
6

删除第k个插入的数:需要多开两个数组存储第k个数是什么

#include <iostream>
#include <string.h>
using namespace std;
const int N = 100010;
int h[N],ph[N],hp[N],sz;
void heap_swap(int a,int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a],hp[b]);
    swap(h[a],h[b]);
}

void down(int u)
{
    int t = u;
    if(u*2<=sz&&h[u*2]<h[t]) t = u*2;
    if(u*2+1<=sz&&h[u*2+1]<h[t]) t = u*2+1;
    if(u!=t)
    {
        heap_swap(u,t);
        down(t);
    }
}
void up(int u)
{
    while(u/2&&h[u/2]>h[u])
    {
        heap_swap(u/2,u);
        u/=2;
    }
}

int main()
{
    int n,m=0;
    scanf("%d",&n);
    while(n--)
    {
        char op[10];
        int k,x;
        scanf("%s",op);
        if(!strcmp(op,"I"))
        {
            scanf("%d",&x);
            sz++;
            m++;
            ph[m] = sz,hp[sz] = m;
            h[sz] = x;
            up(sz);
        }
        else if(!strcmp(op,"PM")) printf("%d\n",h[1]);
        else if(!strcmp(op,"DM"))
        {
            heap_swap(1,sz);
            sz--;
            down(1);
        }
        else if(!strcmp(op,"D"))
        {
            scanf("%d",&k);
            k = ph[k];
            heap_swap(k,sz);
            sz--;
            down(k),up(k);
        }
        else
        {
            scanf("%d%d",&k,&x);
            k=ph[k];
            h[k] = x;
            down(k);
            up(k);
        }
    }
    return 0;
}

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

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

相关文章

大文件上传如何做断点续传

大文件上传如何做断点续传 一、是什么 不管怎样简单的需求&#xff0c;在量级达到一定层次时&#xff0c;都会变得异常复杂 文件上传简单&#xff0c;文件变大就复杂 上传大文件时&#xff0c;以下几个变量会影响我们的用户体验 服务器处理数据的能力请求超时网络波动 上…

信息安全3——数字签名和认证

1 &#xff09;签名&#xff1a;手写签名是被签文件的物理组成部分&#xff0c;而数字签名不是被签消息的物理部分&#xff0c;因而需要将签名连接到被签消息上。 2 &#xff09;验证&#xff1a;手写签名是通过将它与其它真实的签名进行比较来验证而数字签名是利用已经公开的验…

年终总结(我心飞翔向)

2022 年度个人总结&#xff08;自由向&#xff09; 前奏 其实在2021年12月底考研前就回家了&#xff0c;回家做毕设。他们考研的那几天回了中北&#xff0c;参加了党支部会议&#xff0c;见证了一批同学的转预转正&#xff1b;收拾了一大波衣服&#xff0c;因为我已经提前想到…

Git(三) - Git 常用命令

一、设置用户签名 说明&#xff1a; 签名的作用是区分不团操作者身份。用户的签名信息在每一个版本的提交信息中能够看到&#xff0c;以此确认本次提交是谁做的。GIT 首次安装必须设置一下用户签名&#xff0c;否则无法提交代码。 注意&#xff1a; 这里设置用户前面和将来登录…

微机原理真题2019年,错题整理

目录 2019年 填空 编程 1​编辑 2 3 练习册的题 2019年 1&#xff1a;在计算机中能够在一组信息中取出所需要的一部分信息的器件是&#xff08;&#xff09; A:触发器 B:寄存器 C:译码器 D:锁存器 2&#xff1a;宏汇编程序中一般由3个段组成&#xff0c;这三…

FreeRTOS实验使用01

1&#xff1a;vTaskList的使用 我使用的时候&#xff0c;如果把pcWriteBuff定义在任务中&#xff0c;程序会卡死&#xff0c;不信你可以尝试一下&#xff0c;所以我就把pcWriteBuff定义到了全局中&#xff0c;才能使用 2&#xff1a;队列问题 场景&#xff1a;创建3个格子的队…

搜狗 workflow异步调度框架(二)HTTP客户端

1.避免进程提前终止 由于任务的启动是异步的&#xff0c;所以任务的执行和主线程的执行是并行的&#xff0c;如果不加任何的控制&#xff0c;那么当主线程执行完所有操作以后直接退出&#xff0c;并且导致整个进程的终止。 WFFacilities::WaitGroup 可以根据情况阻塞线程或者恢…

DDR3 数据传输 (六)

引言 前文链接: DDR3 数据传输 (一) DDR3 数据传输 (二) DDR3 数据传输 (三) DDR3 数据传输 (四) DDR3 数据传输 (五) 本文在前文设计的基础上,给出板级验证。<

Spring Boot MongoDB 入门

1. 概述 2. 快速入门 3. 基于方法名查询 4. 基于 Example 查询 5. MongoTemplate 6. 自增主键 666. 彩蛋 1. 概述 可能有一些胖友对 MongoDB 不是很了解&#xff0c;这里我们引用一段介绍&#xff1a; FROM 《分布式文档存储数据库 MongoDB》 MongoDB 是一个介于关系数据…

《计算机视觉》:角点检测与图像匹配

文章目录 任务一:基本处理-Harris角点检测原理代码结果与分析任务二:SIFT算法原理代码结果与分析任务一:基本处理-Harris角点检测 数据:棋盘图片 要求:自己写函数实现Harris角点检测子,设置不同参数,比较检测结果 边缘检测子:sobel检测子 响应函数参数alpha:0.05 参数…

【JavaScript】BOM 概念及相关操作

文章目录【JavaScript】BOM 概念及相关操作一. BOM概念BOM可以操作的内容二.window内置对象和属性(1) 获取浏览器窗口的尺寸(2) 获取文档窗口的尺寸(3) 浏览器的常见事件(4) 浏览器的历史记录(5) 浏览器的标签页(6) 浏览器卷去的尺寸(7) 浏览器滚动到的位置浏览器滚动到的位置案…

ARM 按键轮询编程实战

一、什么是按键 1、按键的物理特性 平时没人按的时候&#xff0c;弹簧把按键按钮弹开。此时内部断开的。有人按下的时候&#xff0c;手的力量克服弹簧的弹力&#xff0c;将按钮按下&#xff0c;此时内部保持接通&#xff08;闭合&#xff09;状态&#xff1b;如果手拿开&…

【应急响应】 - Windows 排查分析

Windows 分析排查1. 文件分析1.1 开机启动文件1.2 temp 临时异常文件1.3 浏览器信息分析1.4 文件时间属性分析1.5 最近打开文件分析2. 进程分析2.1 可疑进程发现与关闭3. 系统信息3.1 windows 计划任务3.2 隐藏账户与发现3.2.1 隐藏账号的建立3.2.2 隐藏账号的删除3.3 补丁查看…

Java开发的党员管理系统党员会议系统党务管理系统

简介 Java开发的大学生党员管理系统&#xff0c;主要功能会议&#xff0c;会议记录&#xff0c;会议主持&#xff0c;设置参会人员&#xff0c;请假申请&#xff0c;会议内容附件上传下载&#xff0c;党费管理&#xff0c;入党积极分子预备党员管理&#xff0c;人员变动&#…

hcip实验

1.搭建拓扑 2.配置IP R14&#xff1a; [r14]ip route-static 0.0.0.0 0 145.1.1.2 [r14]acl 2000 [r14-acl-basic-2000]rule permit source any [r14]int GigabitEthernet 0/0/1 [r14-GigabitEthernet0/0/1]nat outbound 2000 [r14]int Tunnel 0/0/0 [r14-Tunnel0/0/0…

【2 - 随机森林 - 原理部分】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

DDR3 数据传输 (四)

目录 引言 AXI从侧接口参数 AXI从侧接口信号 参考说明 引言 前文链接

【数学思维】数理经济中一些基本概念

【数学思维】数理经济中一些基本概念开集 open set 与闭集 closed set紧集 compact set集合有界 bounded set度量空间 metric space欧式空间 euclidean space闭包 closure上包络 upper envelope、下包络 lower envelope上极限 limit superior、下极限 limit inferior左连续、右…

RabbitMQ第五个实操小案例——主题交换机(TopicExchange)

文章目录RabbitMQ第五个实操小案例——主题交换机&#xff08;TopicExchange&#xff09;RabbitMQ第五个实操小案例——主题交换机&#xff08;TopicExchange&#xff09; TopicExchange 和 DirectExchange 这两种交换机非常相似&#xff0c;Topic类型的Exchange与Direct相比&…

JavaScript 面向对象的编程 (Code with mosh学习笔记)

JavaScript OOP Getting Start - 1- What is OOP 面向对象的编程是一种编程范例围绕对象而不是函数一些OOP语言 C#JavaRubyPythonJavaScript Getting Start - 2- Four Pillars of OOP OOP的4个概念&#xff1a; 封装 使用封装重新组合相关的变量和函数减少复杂性增加代码…