【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化

这篇具有很好参考价值的文章主要介绍了【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

字符串匹配算法是在实际工程中经常遇到的问题,也是各大公司笔试面试的常考题目,本文主要介绍BF算法(最好想到的算法,也最好实现)和KMP算法(最经典的)

一、BF算法

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和T的第二个字符,若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力法。                                               ---这段话来自百度百科

这段话晦涩难懂,需要例子支持。

下面我们就通过例子来解释这个问题。 l假定我们给出字符串“ababcabccabcacbab”作为主串,然后给出子串:“abcac”现在我们需要查找子串是否在主串中出现,出现返回主串中的第一个匹配的下标,失败返回-1;

1.图解:

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

2.代码实现: 

思路:

分别用 i 和 j 来遍历 主串 和 子串 ;

当主串和子串字符相同 i++ ,j++ ;

不同时 i = i - j +1 (i从下一个i开始继续遍历) j = 0(子串回到开头);

直到 j >= lenSub (子串遍历完了) 返回 i - j (主串中开始匹配的其实位置)

在Java中str == null和str.length == 0的区别:

str == null表示 str 没有指向任何对象,就是没有对应堆中对象

str.length() == 0表示 str 指向一个字符串对象,但是这个字符串长度为0

//str代表主串 sub代表子串
    public static int BF(String str, String sub) {
        if (str == null || sub == null) {
            return -1;
        }
        int lenStr = str.length();
        int lenSub = sub.length();
        if (lenStr == 0 || lenSub == 0) {
            return -1;
        }
        int i = 0;//遍历主串
        int j = 0;//遍历子串

        while (i < lenStr && j < lenSub) {
            if (str.charAt(i) == sub.charAt(j)) {
                i++;
                j++;
            } else {
                i = i - j + 1;
                j = 0;
            }
        }
        //子串遍历完了
        if (j >= lenSub) {
            return i - j;
        }
        return -1;
    }

二、KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特莫里斯一普拉特操作(简称KMP算法) 。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next( )函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) 

                                                                                                              ---这段话来自百度百科

 1. KMP算法解决的问题

对某些情况下的BF算法进行优化

BF算法每次字符串匹配失败,子串的 j 都会回到子串的第一个字符,但是我们看下面这个图会发现在有些情况下这样的回退是没必要的:【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

当 i 和 j 都匹配到下标为5的字符时,发现主串和字串的字符不匹配,BF算法在此时就会将i 回退到主串下标1字符b,j回退到子串0下标重新进行匹配,既然是匹配到最后一个字符才失败,那么 i 前面和 j 前面一定有一部分是相同的,这里相同部分就是主串0,1和3,4下标都是ab字符串,我们发现此时 j 回退到2下标c位置重新开始合适,i 直接不回退

区别: KMP 和 BF 唯一不一样的地方在,我主串的 i 并不会回退,并且 j 也不会移动到 0 号位置,而是回退到一个特殊的位置

2.图解演示:

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机


3. 为什么主串 i 不回退? 

在下面这种情况下,在下标2位置匹配失败,i 即使回退到1位置也是没有必要的,因为 i回退到1位置的字符b  和 子串下标0位置的字符a  也不一样

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机


4. j 的怎么进行位置的回退——引出next数组

从上面KMP算法解决的问题可知:

此时匹配失败,我们不回退 i ,因为在这个地方匹配失败,说明 i 的前面和 j 的前面,是有部分是相同的,不然两个下标不可能走到这里来,所以 j 回退到2下标,i 不回退,这就是最好的情况

那么我们怎么知道 j 回退到哪个位置呢?由此引入了next数组

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

KMP 的精髓就是 next 数组: 这个数组用来保存某个位置匹配失败后,回退的位置

也就是用 next[ i ] = k来表示,不同的 i 来对应一个k值, 这个 k 就是你将来要移动的i要移动的位置

就拿上面的例子来说,j 回退到2下标 那么next数组中 next [ 5 ] = 2


而 K 的值是这样求的(求next数组):

(1) 规则: 在子串中找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 开始,另一个以-1 下标结尾。
(2) 不管什么数据 next[0]= -1;next1]= 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始(也有些地方next[0]= 0;next1]= 1)

同样以上面的子串 abcabc 为例,求他的next数组:

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

下标0和下标1是固定的,那就不用说

下标2 :j 处于下标2 ,我们就看有没有一个字符串 以下标0(a字符)开始 ,另一个字符串以下标 -1(b字符)结束 的两个相同的字符串 ab这三个字符中肯定没有 所以next [2] = 0

下标3:j 处于下标3 ,我们就看有没有一个字符串 以下标0(a字符)开始 ,另一个字符串以下标 -1(c字符)结束 的两个相同的字符串 abc这三个字符中肯定没有 所以next [3] = 0

下标4:j处于下标4,我们同样看 有没有一个字符串 以下标0(a字符)开始 ,另一个字符串以下标 -1(a字符)结束 的两个相同的字符串 abca这三个字符中是有相同字符串a的 所以next [4] = 1(这里的1代表相同字符串的长度,没有就为0)

下标5 :j处于下标5 abcab 中ab 为相同的(一个a开头 另一个b结尾)字符串 所以next [5] = 2


求next数组的练习: 跟上面的过程一样,如果不懂可以去看 博哥视频讲解的KMP算法 30min的位置

练习 1: 举例对于”ababcabcdabcde”,求其的 next 数组?

答案:                 -10012012001200

练习 2: 再对”abcabcabcabcdabcde”,求其的 next 数组?

答案:         -10001 2345678901230

一般情况答案都是next[0]= 0;next1]= 1,所以我们在此答案基础上全部+1即可

从上面的答案我们可以得出结论:数组在增的时候都是一个一个+1,不可能跳着加


到这里大家对如何求next数组应该问题不大了,接下来的问题就是 :

5.已知next[ i ] = k;怎么求next[i+1]=? 

如果我们能够通过 next [ i ]的值,通过一系列转换得到 next [ i+1]得值,那么我们就能够实现这部分

首先假设: next[ i ] = k 成立 (为了方便数组名命名为p)

那么,就有这个式子成立:p [ 0 ]...p [ k-1 ] = p [ x ] ..p [ i-1 ]

因为 i -1 -k = k -1 那么 x = i - k ,也就是p [ 0 ]...p [ k-1 ] = p [ i - k ] ..p [ i-1 ]

到这一步: 我们再假设如果 p [ k ] = p [ i ] ;在上面得到的式子两边加上这个式子
我们可以得到p [ 0 ]...p [ k ] = p [ i-k ] ..p [ i ] ;那这个就是 next[ i+1]= k+1;

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

那么: p[ i ] != p[ k ] 呢?

看如下实例:

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

一次不匹配 ,j 回退到 2下标位置 不一定是你要找的 

继续回退 此时回退到了0下标 (也就是说 k一直回退 去找 p [i] == p [k] ,这样就满足了p [ k ] = p [ i ])


6.KMP算法代码实现

//找到子串在主串当中的下标
    public static int KMP(String str,String sub,int pos) {
        if(str == null||sub == null) return -1;

        int lenStr = str.length();
        int lenSub = sub.length();
        if(lenStr == 0||lenSub == 0) return -1;

        if(pos<0 || pos >= lenStr) return -1;

        int [] next = new int[lenSub];
        getNext(sub,next);

        int i = pos;//从pos位置开始遍历主串
        int j = 0;//遍历子串

        while(i < lenStr && j <lenSub) {
            //这里要考虑到一开始就不匹配,j=-1
            if (j==-1||str.charAt(i) == sub.charAt(j)) {
                i++;
                j++;
            } else {
                //下标不一样,一直回退
                j = next[j];
            }
        }
        if(j==lenSub) {
            return i-j;
        }
        return -1;

    }
    //重点:求子串的next数组
    public static void getNext(String sub,int [] next) {
        next[0] = -1;
        next[1] = 0;
        int i = 2;//i表示所求next数组的下标,是提前走了一步的
        int k = 0;//比较是否相等的前一项的k
        //这里next[i]就是要求的,和我们分析的next[i+1]一样
        // 原来判断的是p[i]==p[k],现在应该判断p[i-1]==p[k]
        while(i < sub.length()) {
            //此处要考虑k回退到了-1位置,next值就为0
            if (k==-1||sub.charAt(i-1) ==sub.charAt(k)) {
                next[i] = k+1;
                k++;
                i++;
            } else {
                //p[i-1]!=p[k],则k继续回退
                k = next[k];
            }
        }
    }

7.next数组的优化

为什么要对next数组进行优化?

有如下串:aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7

假设5位置匹配失败,那么就得回退到4位置,4位置和5位置都是a,那么还得回退到3位置,而3位置和4位置都是a,还得继续回退,就这样一直回退到0位置,由此引入了nextval数组进行了优化

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

next 数组的优化,即如何得到 nextval 数组:

(1)回退到的位置和当前字符一样,就写回退那个位置的nextval值

(2)如果回退到的位置和当前字符不一样,就写当前字符原来的next值

就以上面字符串为例:

0下标:肯定还是为-1

1下标:这个位置回退到0位置,因为这个位置的值和0位置(回退的位置)的值一样,所以这个位置的值就写回退位置的值(即-1)

2-7下标:这些位置回退到前一个位置,值都是一样的,所以都是-1

8下标:  回退到的位置和当前字符不一样,直接写next[ 8 ]的值7即可

则修正后的数组 nextval 是:-1, -1,-1,-1, -1, -1, -1, -1,7。


练习: 模式串 t='abgabbcabcaabdab’,该模式串的 next 数组的值为 ( D )nextva1 数组的值为 (F)

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

答案:在下面答案的基础上+1即可选择

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机

  这里也不做过多的解释,过程跟上面一样,不懂的可以评论区或者私信问我,或者 看博哥视频讲解的KMP算法 2h的位置


本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 !

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化,数据结构与算法,数据结构,linux,运维,服务器,人工智能,算法,智能手机 文章来源地址https://www.toymoban.com/news/detail-766328.html

到了这里,关于【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JavaScript数据结构与算法】字符串类(反转字符串中的单词)

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端(Node.js) 📃 个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力 n 年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目

    2023年04月09日
    浏览(74)
  • 数据结构与算法--字符串(单选题)

    1、令s=\\\"abcabaa\\\",则它的特征向量next函数值和优化特征向量nextval函数值为(下标从0开始): A.next={0,1,1,1,2,3,2},nextval={0,1,1,0,1,2,1} B.next={-1,0,0,-1,0,2,1},nextval={-1,0,0,0,1,2,1} C.next={-1,0,0,0,1,2,1},nextval={-1,0,0,-1,0,2,1} D.next={-1,0,0,0,1,2,1},nextval={-1,0,0,0,1,2,1} C 规定next[1]=0 s[2]前,“a”,next[2]=重合

    2024年02月07日
    浏览(35)
  • 数据结构--字符串的KMP算法

    朴素模式匹配算法: 一旦发现当前这个子串中某个字符不匹配,就只能转而匹配下一个子串(从头开始) 但我们可以知道: 不匹配的字符之前,一定是和模式串一致的 color{red}不匹配的字符之前,一定是和模式串一致的 不匹配的字符之前,一定是和模式串一致的 我们可以利用

    2024年02月12日
    浏览(47)
  • 数据结构(C语言):两个字符串比较大小

    在写这篇文章之前,作者想先和大家分享一个小故事。如果你不想看这个小故事的话,可以直接跳到第二点哦。 为了锻炼自己的编码能力,平时作业和实验题的代码我都是不看书、不看老师的PPT,按照自己的思路一行一行敲出来的。同时也不太理解那些照着书敲代码的同学。

    2024年02月03日
    浏览(34)
  • Redis数据结构与对象-字符串对象SDS

    Redis没有使用C的字符串,而是自己构建了简单动态字符串(Simple Dynamic String),简称SDS。通过这种字符串格式能够对redis字符串操作进行提速。下面介绍原理。 sds数据格式如下: 比如,一个sds 中存的是 “Redis” ,那么buf 中是一个char型的数组,存5个字符R, e,d,i,s len =5;free

    2023年04月16日
    浏览(34)
  • 数据结构与算法之字符串: Leetcode 557. 反转字符串中的单词 III (Typescript版)

    翻转字符串中的单词 III https://leetcode.cn/problems/reverse-words-in-a-string-iii/ 描述 给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 示例 1: 示例 2: 提示: 1 = s.length = 5 * 1 0 4 10^4 1 0 4 s 包含可打印的 ASCII 字符。 s 不包含任何开头或

    2024年02月01日
    浏览(63)
  • MATLAB 之 常用内部函数,运算,字符串和结构数据与单元数据

    内部函数是由 MATLAB 系统根据一般用户的需要编制并提供给用户使用的一组程序,也被称为系统函数或库函数。 MATLAB 提供了许多数学函数,函数的自变量规定为矩阵变量,运算法则是将函数逐项作用于矩阵的元素上,因而运算的结果是一个与自变量具有相同维数和大小的矩阵

    2024年02月04日
    浏览(38)
  • 【零基础学Rust | 基础系列 | 数据结构】元组,数组,向量,字符串,结构体

    在Rust编程语言中,数据结构是组织和存储数据的一种方式,它们使得数据可以高效地被访问和操作。本章将详细介绍元组,数组,向量,字符串,和结构体这几种基本的数据结构。 元组是Rust编程语言中的一种复合数据类型,它可以包含多个值,这些值可以是不同类型。元组

    2024年02月11日
    浏览(42)
  • 数据结构(Java实现)-字符串常量池与通配符

    字符串常量池 在Java程序中,类似于:1, 2, 3,3.14,“hello”等字面类型的常量经常频繁使用,为了使程序的运行速度更快、更节省内存,Java为8种基本数据类型和String类都提供了常量池。 “池” 是编程中的一种常见的, 重要的提升效率的方式, 我们会在未来的学习中遇到各

    2024年02月10日
    浏览(40)
  • 【JavaScript数据结构与算法】字符串类(计算二进制子串)

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端(Node.js) 📃 个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力 n 年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目

    2024年02月05日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包