要解决的问题

假设字符串str长度为N,字符串match长度为M,M <= N, 想确定str中是否有某个子串是等于match的。返回和match匹配的字符串的首字母在str的位置,如果不匹配,则返回-1

OJ可参考:LeetCode 28. 实现 strStr()

暴力方法

从str串中每个位置开始匹配match串,时间复杂度O(M*N)

KMP算法

KMP算法可以用O(N)时间复杂度解决上述问题。

流程

我们规定数组中每个位置的一个指标,这个指标定义为

这个位置之前的字符前缀和后缀的匹配长度,不要取得整体。

例如: ababk 这个字符串,k位置的指标为2, 因为k之前位置的字符串为abab

前缀ab 等于 后缀ab,长度为2,下标为3的b的指标为1,因为b之前的字符串aba ,前缀a 等于后缀a, 长度为1。

人为规定:0位置的指标是-1,1位置的指标0

假设match串中每个位置我们都已经求得了这个指标值,放在了一个next数组中,这个数组有助于我们加速整个匹配过程。

我们假设在某个时刻,匹配的到的字符如下

其中str的i..j一直可以匹配上match串的0...m, str中的x位置和match串中的y位置第一次匹配不上。如果使用暴力方法,此时我们需要从str的i+1位置重新开始匹配match串的k位置,而KMP算法,利用next数组,可以加速这一匹配过程,具体流程是,依据上例,我们可以得到y位置的next数组信息,假设ynext数组信息是2,如下图

如果ynext数组信息是2,那么0...k 这一段完全等于f...m这一段,那么对于match来说,当y位置匹配不上x位置以后, 可以直接让x位置匹配ynext数组位置p上的值,如下图

如果匹配上了,则x来到下一个位置,p来到下一个位置继续匹配,如果再次匹配不上,假设p位置的next数组值为0, 则继续用x匹配pnext数组位置0位置上的值,如下图

如果x位置的值依旧不等于0位置的值,则宣告本次匹配失败,str串来到x下一个位置,match串从0位置开始继续匹配。

next数组求解

next数组的求解是KMP算法中最关键的一步,要快速求解next数组,需要做到当我们求i位置的next信息时,能通过i-1next数组信息加速求得,如下图

当我们求i位置的next信息时,假设j位置的next信息为6,则表示

m...n这一段字符串等于s...t这一段字符,此时可以得出一个结论,如果:

x位置上的字符等于j位置上的字符,那么i位置上的next信息为j位置上的next信息加1,即为7。如果不等,则继续看x位置上的next信息,假设为2,则有:

此时,判断q位置的值是否等于j位置的值,如果相等,那么i位置上的next信息为x位置上的next信息加1,即为3,如果不等,则继续看q位置上的next信息,假设为1,那么有

此时,判断p位置的值是否等于j位置的值,如果相等,那么i位置上的next信息为q位置上的next信息加1,即为2,如果不等,则继续如上逻辑,如果都没有匹配上j位置的值,则i位置的next信息为0。

主流程代码复杂度估计

public class LeetCode_0028_ImplementStrStr {
public static int strStr(String str, String match) {
if (str == null || match == null || match.length() > str.length()) {
return -1;
}
if (match.length() < 1) {
return 0;
}
char[] s = str.toCharArray();
char[] m = match.toCharArray();
int l = m.length;
int[] next = getNextArr(m, l);
int x = 0;
int y = 0;
while (y < s.length && x < l) {
if (s[y] == m[x]) {
y++;
x++;
} else if (x != 0) {
x = next[x];
} else {
y++;
}
}
return x == l ? y - x : -1;
} // 求解next数组逻辑
private static int[] getNextArr(char[] str, int l) {
if (l == 1) {
return new int[]{-1};
}
int[] next = new int[l];
next[0] = -1;
next[1] = 0;
int i = 2; // 目前在哪个位置上求next数组值
int cn = 0; // 前后缀最长字符的长度,也表示下一个要比的信息位置
while (i < next.length) {
if (str[i - 1] == str[cn]) {
next[i++] = ++cn;
} else if (cn > 0) {
cn = next[cn];
} else {
next[i++] = 0;
}
}
return next;
}
}

next数组的求解流程时间复杂度显然为O(N),现在估计主流程的复杂度,主流程中,x能取得的最大值为str字符串的长度N,定义一个变量x-y,能取得的最大值不可能超过N(即当x = N,y=0时候),在主流程的wile循环中,有三个分支

        while (y < s.length && x < l) {
if (s[y] == m[x]) {
y++;
x++;
} else if (x != 0) {
x = next[x];
} else {
y++;
}
}

我们考虑这三个分支对于yy - x变化范围的影响

分支 y y - x
x++; y++ 推高 不变
x = next[x] 不变 推高
y++ 推高 推高

如上分析,yy-x都不可能降低,且三个分支只能中一个,所以,而yy-x的最大值均为N,所有分支执行总推高的次数不可能超过2N。即得出主流程的复杂度O(N)

KMP算法应用

求一个字符串的旋转词(详见:LeetCode 796)

思路

将这个字符串拼接一下, 比如原始串为:123456,拼接成:123456123456

如果匹配的字符串是这个拼接的字符串的子串,则互为旋转词。

一棵二叉树是否为另外一棵二叉树的子树(详见:LeetCode 572)

思路

先将两棵树分别序列化为数组A和数组B,如果B是A的子串,那么A对应的二叉树中一定有某个子树的结构和B对应的二叉树完全一样。

更多

算法和数据结构笔记

参考资料

KMP算法解决字符串匹配问题的更多相关文章

  1. Sunday算法解决字符串匹配问题

    概述 提起字符串匹配可能更多人会想到KMP算法,该算法时间复杂度为O(m+n),而且也是我们在学习数据结构过程中最早接触到的比较好的算法.但KMP算法需要在模式字符串有关联的情况下,也即模式字符串前后 ...

  2. 【KMP算法】字符串匹配

    一.问题 给定两个字符串S(原串)和(模式串)T,找出T在S中出现的位置. 二.朴素算法 当S[i] != T[j]时,把T往后移一位,回溯S的位置并重新开始比较.    (1) 成功匹配的部分(AB ...

  3. 【算法】字符串匹配之Z算法

    求文本与单模式串匹配,通常会使用KMP算法.后来接触到了Z算法,感觉Z算法也相当精妙.在以前的博文中也有过用Z算法来解决字符串匹配的题目. 下面介绍一下Z算法. 先一句话讲清楚Z算法能求什么东西. 输 ...

  4. Java实现 蓝桥杯 算法提高 字符串匹配

    试题 算法提高 字符串匹配 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行.你的程序还需支持大小写敏感选项:当选项打开时,表示同一个字母的大写和小写看作不同的字符:当选项关闭时 ...

  5. 【数据结构与算法】字符串匹配(Rabin-Karp 算法和KMP 算法)

    Rabin-Karp 算法 概念 用于在 一个字符串 中查找 另外一个字符串 出现的位置. 与暴力法不同,基本原理就是比较字符串的 哈希码 ( HashCode ) , 快速的确定子字符串是否等于被查 ...

  6. 运用kmp算法解决的一些问题的简单题解

    学习kmp算法我最后是看的数据结构书上的一本教材学会的..我认为kmp相对于普通的BF算法就是避免了非常多不必要的匹配.而kmp算法的精髓自然就在于next数组的运用...而next数组简而言之就是存 ...

  7. Boyer Moore算法(字符串匹配)

    上一篇文章,我介绍了KMP算法. 但是,它并不是效率最高的算法,实际采用并不多.各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法. Boyer-Mo ...

  8. C++编程练习(7)----“KMP模式匹配算法“字符串匹配

    子串在主串中的定位操作通常称做串的模式匹配. KMP模式匹配算法实现: /* Index_KMP.h头文件 */ #include<string> #include<sstream& ...

  9. 利用KMP算法解决串的模式匹配问题(c++) -- 数据结构

    题目: 7-1 串的模式匹配 (30 分) 给定一个主串S(长度<=10^6)和一个模式T(长度<=10^5),要求在主串S中找出与模式T相匹配的子串,返回相匹配的子串中的第一个字符在主串 ...

随机推荐

  1. Visio操作【未完】

    Visio 1.如何操作文档 新建基本框图和空白框图 单击基本框图打开后有模具 空白框图打开之后并没有形状 左下角发现有 更改纸张方向大小 自动调整大小: 如果我们选择形状进入到我们的页面,如果放到边 ...

  2. netty系列之:自定义编码解码器

    目录 简介 自定义编码器 自定义解码器 添加编码解码器到pipeline 计算2的N次方 总结 简介 在之前的netty系列文章中,我们讲到了如何将对象或者String转换成为ByteBuf,通过使用 ...

  3. Build Web Server with Apache and Passenger

    Follow the instructions at 2.6. Generic installation, upgrade and downgrade method: via tarball of P ...

  4. 零基础学Java之Java学习笔记(一):Java概述

    什么是Java? Java是一门面向对象编程语言,可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序. Java特点有哪些? 1.Java语言吸收了C++语言的各种优点,具有功能强大 ...

  5. Python - typing 模块 —— 类型别名

    前言 typing 是在 python 3.5 才有的模块 前置学习 Python 类型提示:https://www.cnblogs.com/poloyy/p/15145380.html 常用类型提示 ...

  6. win7环境下配置JDK&&安装Weblogic12.2.1.4.0

    win7环境下安装Weblogic12.2.1.4.0 写在前面 最近因为想复现一下weblogic的CVE-2020-2555和CVE-2020-2883漏洞,需要weblogic环境,但是vulh ...

  7. WPF 饼状图,柱形图,折线图 (2 折线图)

    折线图在柱形图的基础上,做了一些修改.大概效果和用法如下. X轴和Y轴的刻度,使用用了Path的Figures属性,绘制多条Figure+LineSegment完成. 同时,由于折线图很可能会画多条线 ...

  8. 异步编程之APM

    一.APM概述 APM即异步编程模型的简写(Asynchronous Programming Model),我们平时经常会遇到类似BeginXXX和EndXXX的方法,我们在使用这些方法的时候,其实就 ...

  9. DQL,DML,DDL,DCL分别是什么?

    SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL. 数据查询语言DQL数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE子句 ...

  10. 等待唤醒机制----线程池----lambda表达式

    1.等待唤醒机制 1.1线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的 ...