数据结构(模式匹配及相关算法)

news2024/10/5 14:01:23

目录

模式匹配

BF算法 

算法实现

算法分析

KMP算法

问题的引入(一)

问题的引入(二)

问题的引入(三)

相关概念

 计算失配函数的算法

算法思路

算法优点


模式匹配

函数int find(const sstring &t, int start )

从字符串(以下称主串s)的第start个位置的字符起,向后查找字符串t第一次在主串中出现的位置。如果在主串中找到该子串,返回其首次出现的位置,否则返回-1。

该操作相当于在主串的所有子串集合中匹配待查找的子串,因此该操作也被称作模式匹配

模式匹配(亦称样品匹配)是各种串处理中最具有代表性的操作。一般被匹配串s称为主串,匹配串t称为模式。

设主串和模式t的串长度分别为n和m;主串值为“s0s1s2s3……sn-1”,模式值为“ t0t1t2t3……tm-1”,起始位置start = 0

模式匹配的两种算法:

Brute-Force(BF)算法

Knuth-Morris-Pratt(KMP)算法

BF算法 

算法实现

 以s =“SHANGHAI”,t=“HAI”为例,n=8,m=3。i指示主串当前字符的下标,j指示模式当前字符的下标。

 

//从串的第start个字符起,向后查找字符串t第一次在
//串中出现的位置找到返回位置序号,未找到返回 -1。
int sstring::BF_find(const sstring &t, int start )const
{ 
    int curStart; //每次比较时,主串的起始位置
    int i,j=0;   int pos=-1;//匹配不成功返回-1    int n, m;
     n = length();   m = t.length(); //主串长度和模式长度
     curStart=start;
 while (curStart<=(n-m))//剩余主串结点长度大于等于模式串长度
 {     i=curStart;
        while ((j<m)&&(str[i]==t.str[j])) {  i++;    j++; }
        if (j==m) {pos = curStart; break; }//匹配成功返回位置
        curStart++;    j=0;
   }
    return pos; 
}  

算法分析

最好情况:

主串从0下标开始比较整个模式即告成功,时间为O(m)

主串中每个位置上的字符都和模式中首字符不等,时间为时间为O(n)

最差情况:

主串从0下标一直到n-m下标开始和模式比较时,每次都是模式的最后一个字符不等,结果为匹配不成功;或者只有当主串从下标为n-m开始和模式比较相等,结果为匹配成功,时间均为        O((n-m)m)。

Knuth-Morris-Pratt(KMP)算法

问题的引入(一)

匹配时,前4个元素相同,第5个不同。因模式中元素各不相同,故下次可以让模式的第1个字符和主串的第5个字符开始比较,中间可以省略。

 根据模式中字符的特征+已经和主串中比较的结果,决定下次比较时主串的起始位置。

问题的引入(二)

匹配时,前4个元素相同,第5个不同。因模式中前4个元素均相同,故下次可以让模式的第4个字符和主串的第5个字符开始比较,中间可以省略。

 根据模式中字符的特征+已经和主串中比较的结果,决定下次比较时模式的起始位置。

问题的引入(三)

观察第三个例子

 根据模式中字符的特征+已经和主串中比较的结果,决定下次比较时模式的起始位置。

相关概念

前缀:一个串的前缀,是指从第1个字符开始到它的任意一个字符为止的子串。

对模式t= t0t1t2t3…tm而言,t0t1t2就是它的长度为3的前缀,t0t1t2t3t4就是它的长度为5的前缀。

失配:当主串中某字符和模式中某字符比较且不等时,称发生失配。主串中发生失配的位置称失配点

注意:索引都是从0开始

最长前缀:图中,s6≠t6,发生失配。观察以模式失配点t6的前一字符t5为结束字符的子串,t0t1t2是它的一个前缀,而失配点t6前有一个子串t3t4t5,有t3t4t5=t0t1t2关系存在,称t0t1t2为该子串的最长前缀。假如模式串为r=“abcdsjtuabf”,则对字符f前的子串来说,最长前缀为r0r1即“ab”。

上图中,第二轮匹配可直接将主串失配点s6 和模式最长前缀t0t1t2的后一字符t3进行比较即可。由于t3 = s6,t4 = s7,t5 = s8,t6 = s9,这样就在主串中找到了模式t。

最长前缀获取:在实际应用中,主串通常比模式长得多,因此在模式匹配开始之前,值得花点时间将模式中字符的情况,完全分析清楚,因此这个任务是模式匹配前的首要任务。

失配函数:对模式中每一个位置上的字符,发现从开始到该位置前一字符形成的子串的最长前缀。下面用一个整型数组next[j]记录模式中第j个字符前子串的最长前缀的长度,以后将next称为失配函数。

 计算失配函数的算法

//返回模式t的失配函数
int* nextValue(const sstring &t)
{
    int *next, m, i, j, k;

    m = t.length();
    if (m==0) return NULL;
    next = new int[m];
    next[0]=-1; next[1]=0;
for (j=2; j<m; j++)
    {   for (k=j-1; k>0; k--)
        {  for (i=0; i<k; i++)
                if (t.str[i]!=t.str[j-k+i]) break;
            if (i==k) break;
        }
        if (k==0)
            next[j] = 0;
        else
            next[j] = k;
    }
return next;
}

算法思路

假设主串s和模式t在匹配过程中,发生了失配,即tjt_j和sis_i不等。

假设以模式失配点的前一字符tj−1t_(j-1)为结束字符的子串的最长前缀长度为k,则主串s的失配点sis_i下一步只需和tkt_k继续比较下去就可以了。若tkt_k=sis_i,则继续比较tk+1t_(k+1)和si+1s_(i+1),如此继续进行,直至找到该模式,或者断定该模式不存在为止。

//从串的第start个字符起,向后
//查找字符串t第一次在串中出现
//的位置找到返回位置序号,未
//找到返回 -1。
int sstring::KMP_find(const sstring &t, int start )const
{
    int n = length(), m = t.length();
    int *next, curStart;
    int i,j;
    int pos=-1;

    next = nextValue(t); 
    //根据next数组中的值在主串中查找模式
    i=start; j=0;
    while (i<=(n-m))
    {  curStart = i-j;
        while ((j<m)&&(str[i]==t.str[j])){i++;j++;}
        if (j==m)
        {  pos=curStart;  break;   }
        else
        {  if (next[j]==-1) {++i;j=0;}
            else {j=next[j];}
        }
    }
    return pos;
}

计算失配函数,时间达O(m3)。

KMP算法,观察主串的指针i:

i开始为0,每次比较后,i不会减少,它或者不变或者增大1,直至n-m。

i不变的次数共有多少?:

如果本次开始匹配主串的第i个字符和模式中第1个字符相等,则i增加1;如果不等,本次模式匹配结束,下次模式匹配时i也增加1,故主串中每个i最多用2次,总体有2(n-m)次比较。

特殊地,如果模式的第1个字符和主串的第1个字符比较时总是不等,比较次数最少,为n-m次。

一般情况,m远小于n,KMP模式匹配算法时间复杂度为O(n)。

 算法优点

在硬盘当中寻找模式时,主串指针i不会变小(即不回溯),是一个很大的优点。

设内存保存了模式和next数组,还保存了从硬盘读来的主串中一个扇区大小的字符串。假设第一个扇区的字符串同模式的相应字符串完全匹配了,下一步将读入第二个扇区的字符串到内存缓冲区,将第一个扇区的字符串覆盖掉;假设已在内存缓冲区中的第二个扇区的字符和模式进行匹配时,发生失配,用BF算法,就要将第一个扇区的字符串重新读入到内存缓冲区才可以继续匹配下去,重新读入非常费时,而KMP算法完全避免了这个缺点,是一个很好的算法。

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

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

相关文章

机器学习(三):人工智能主要分支

文章目录 人工智能主要分支 一、计算机视觉 二、语音识别 三、文本挖掘/分类 四、机器翻译 五、机器人 人工智能主要分支 通讯、感知与行动是现代人工智能的三个关键能力&#xff0c;在这里我们将根据这些能力/应用对这三个技术领域进行介绍&#xff1a; 计算机视觉(CV…

WiFi monitor模式的配置和运行检查(Ubuntu系统)

WiFi monitor模式的配置和运行检查1. WiFi monitor模式介绍2. WiFi monitor模式查看Step1&#xff1a;确保计算机上有安装硬件WiFi无线网卡Step2&#xff1a;安装必要的工具Step 3&#xff1a;iw list查看无线网卡是否支持monitor模式Step 4&#xff1a;配置WiFi monitor模式St…

有了独自开,我们离自己开发一套系统还会远吗

目录 一、结识独自开 二、独自开的介绍 三、独自开的需求 四、独自开注册流程 五、神仙公司独自开 一、结识独自开 算是机缘巧合&#xff0c;我被C站白佬拉入了他的聊天群&#xff0c;群内均是来自于CSDN的不同领域的优质作者&#xff0c;其中不乏相关领域工作多年的老工程…

“任性”华为 | 七十八岁老人的“四渡赤水”(二)

导读华为是一家“任性”的企业——因为任正非将自己的性格赋予了华为——在企业前进的每一个路口&#xff0c;都会看见这种性格的印记。2022年12月&#xff0c;当美国总统拜登出现在凤凰城出席台积电工厂迁机仪式上&#xff0c;苹果公司首席执行官库克等约900名政商界人士前往捧…

c#检测网络连接信息

用手机全屏看B站视频时可以看到右上角标识有WIFI&#xff0c;比较好奇如何检测当前网络连接是wifi还是数据网络什么的。于是百度相关信息&#xff0c;找到参考文献1-2&#xff0c;其中介绍采用Xamarin.Essentials检测网络连接性&#xff0c;其中的Connectivity类可用于监视设备…

【MATLAB】三维旋转的实现

1 三维旋转的表达方式 三维空间中常用的表示旋转的方式有&#xff1a; **[1]旋转矩阵(rotation matrix) [2]旋转向量(rotation vector&#xff09;/角轴&#xff08;轴角&#xff09;(axis angle) [3]欧拉角(euler angles) [4]四元数(quaternion)**主动旋转和被动旋转&#x…

现场工程师出手-PCAPHub与云IP实现异地LAN工业联测

在去年&#xff0c;因为众所周知的因素影响&#xff0c;项目的甲方主动提出延缓设备的交付。作为乙方&#xff0c;尽管项目延缓是甲方提出的&#xff0c;但依旧希望按期交付&#xff0c;这样才能回款&#xff0c;熬过一年。其实&#xff0c;2022年初&#xff0c;几类传感器、压…

Visual Studio 17.5 拼写检查器预览版现已推出,来说说你的看法吧

写在前面&#xff1a; Visual Studio17.5版本已添加拼写检查器功能&#xff0c;Visual Studio 中的许多功能旨在帮助你编写所需的代码。Visual Studio帮助你确保代码的编译&#xff0c;甚至可以帮助代码样式。现在它甚至可以确保您的拼写准确。Visual Studio 17.5 preview 3 引…

Linux常用命令——talk命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) talk 让用户和其他用户聊天 补充说明 talk命令是talk服务器的客户端工具&#xff0c;通过talk命令可以让用户和其他用户聊天。linux中talk命令参数程序的使用很简单&#xff0c;只要知道交谈对象的地址&#x…

plt自定义主要刻度值和次要刻度值

使用set_xticks方法和set_xticklabels方法即可 1、set_xticks方法 参数说明&#xff1a; set_xticks(self, ticks, minorFalse)参数1&#xff1a;ticks&#xff1a;指定刻度出现的位置参数2&#xff1a;minor&#xff1a;指定是否是次要刻度返回值1&#xff1a;包含XTick实例…

【数学建模】华为杯研究生数学建模备赛的一些建议

文章目录前言一、建模题目介绍1.1、题目数量1.2、题目种类1.3、题目难度1.4、题目选择二、笔者的备赛过程2.1.简单的题目&#xff08;本科比赛&#xff0c;学科大作业&#xff09;2.2.真题三、编程的备赛建议3.1.matlab和python的基础语法3.2.数据预处理3.3.常用的机器学习算法…

win10系统新增的几款非常有用的基础快捷键

win就是键盘的这个键winq或wins 打开搜索winw 打开白板、全屏截图wint 配合左右箭头←→&#xff0c;来回切换最小化窗口在任务栏的缩略图winu 设置显示器wini 打开设置winp 屏幕投影、扩展屏幕wina 查看最近消息通知winh 语音输入法wink 查找设备&#xff08;如&#xff1a;打…

线程池默认大小为CPU核数的2倍

1、前言 有位工作5年的小伙伴问我说&#xff0c;为什么Netty线程池默认大小为CPU核数的2倍&#xff0c;今天&#xff0c;我花2分钟时间给大家专门分享一下我对这个问题的理解。 另外&#xff0c;我花了1个多星期把往期的面试题解析配套文档准备好了&#xff0c;想获取的小伙伴…

Introduction to Multi-Armed Bandits——03 Thompson Sampling[1]

Introduction to Multi-Armed Bandits——03 Thompson Sampling[1] 参考资料 Russo D J, Van Roy B, Kazerouni A, et al. A tutorial on thompson sampling[J]. Foundations and Trends in Machine Learning, 2018, 11(1): 1-96. ts_tutorial 在线学习(MAB)与强化学习(RL)[…

JavaScript基础知识点整理(一)——数据类型、判定、转换、this指向

JavaScript是每一位前端开发者都必备的技能&#xff0c;接下来会分章节文章阐述介绍每一部分的内容。 JavaScript基础整理①1、JavaScript类型2、原始&#xff08;primitive&#xff09;类型3、对象&#xff08;Object&#xff09;类型4、类型判定4.1、typeof4.2、instanceof4.…

Vue2基础、组件化编程、脚手架、Vuex、Vue路由、UI组件库

尚硅谷张天禹老师讲课 学习视频 1、Vue简介 Vue2中文官网 1.1 Vue 介绍 一套用于构建用户界面的渐进式JavaScript框架 构建用户界面&#xff1a;把数据通过某种办法变成用户界面渐进式&#xff1a;可以自底向上逐层的应用&#xff0c;简单应用只需要一个轻量小巧的核心库&…

SQL--DQL

目录 1、基础查询 1. 查询多个字段 1. 举例 2. 举例 2. 字段设置别名 1. 举例 2. 举例 3. 去除重复记录 1. 举例 2、条件查询 1. 等于&#xff08;&#xff09; 2. 小于&#xff08;<&#xff09; 3. 小于等于&#xff08;<&#xff09; 4. 没有&#xff…

Java设计模式中命令模式是怎么回事/命令模式如何使用,什么场景适用

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 6.4 命令模式 6.4.1 定义 将一个请求封装为一个对象&#xff0c;使发出请求的责任和执行请求的责任分隔开&#xff0c;方便了将命令对象进行存储&#xff0c;传递…

激活函数(26个)

最近在学习网络&#xff0c;发现一会这个网络用了这个激活函数&#xff0c;一会那个网络用了那个激活函数&#xff0c;这些激活函数都有什么作用啊&#xff0c;不知道&#xff0c;这里学习一下&#xff0c;整理下来&#xff0c;方便以后查阅。 激活函数&#xff08;26个&#x…

Elasticsearch7.8.0版本高级查询——组合查询文档

目录一、初始化文档数据二、组合查询文档2.1、概述2.2、示例一、初始化文档数据 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 &#xff1a;http://localhost:9200/user/_doc/1&#xff0c;请求体内容为&#xff1a; {"name":"张三","age"…