题目

给定一个字符串\(S\),求\(M\)字符串是否是\(S\)字符串中的子串.如果是,返回\(M\)对应\(S\)的第一个下标,否则返回-1.

例如:S串为a b c d a b c d a b c d e

M串为a b c d e

结果:返回S串下标8.

个人想法

之前看过这种求子串的题,但是只在脑海中想象了一下,没有动手写出算法.

看到的时候心里就嘀咕肯定不能暴力求解,需要让子串\(M\)进行跳跃,但是没有具体地研究边界问题,也没有动手写一个字符串验证想法,导致想法很不靠谱.

之前的想法很粗糙:

挨个比较字符串,如果不相同,整体都跳跃,跳跃的方式是\(M\)串首位对其不匹配\(S\)串的下标位置.然后继续比较

大约就是这个样子,想到这就没继续往下想也没有去实现,这显然是错误的.跳跃是应该跳跃,但是不是那么粗糙的跳跃,假如字符串和子串是下面这种情况这种情况:

以我那种错误的想法是会产生bug的:

结果是\(S\)串不会包含\(M\)串,但事实上是包含的.(下文将目标串统一称作\(S\),子串统一称作\(M\))

那我们就应该考虑如何正确的跳跃.

KMP算法

知道了之前的想法是错误的,我们就应该找到符合各种边界的跳跃方式.只要确定了跳跃方式,KMP就很容易写出来了.KMP中提到了前缀表(或者最大相同前后缀)这些概念.前缀表暂时先不考虑,只谈谈如何进行跳跃才能满足边界.

继续回到之前的图,如果我们想要成功地找到下标,正确的跳跃方式应该是下图这样:

注意\(M\)串错误匹配项(\(M[5]\)元素d)之前的元素(蓝色填充的a,b).

好,为什么这么跳呢?

当确定\(S[5]\)和\(M[5]\)不匹配的时候,我们可以确定d之前的项都是匹配的.即\(S[0-4]\)和\(M[0-4]\)是匹配的.

​ \(M[0-4]\)

对于已经匹配的\(M[0-4]\),前缀几项与后缀几项相同(即\(M[0]\),\(M[1]\)和\(M[3]\),\(M[4]\)是相同的),那么把\(M\)平移使\(M[0]\)对齐\(M[3]\)之前所处的位置,这一定能确保\(M[0]\),\(M[1]\)和之前\(M[3]\),\(M[4]\)与\(S\)对应的\(S[3]\),\(S[4]\)是匹配的,即一定能确保\(M[0]\),\(M[1]\)和\(S[3]\),\(S[4]\)是匹配的.

平移

那我们如何利用这个特点跳跃呢,现在我们生成一个辅助的前后缀相同的数组.

a,b,c,a,b,d为例,

M产生的子串 相同前后缀个数 备注:就把相同前后缀数组称为\(D\)
a 0
a,b 0
a,b,c 0
a,b,c,a 1
a,b,c,a,b 2
a,b,c,a,b,d 0 最后一个匹配就可以返回了,所以用处不大

最后生成一个相同前后缀的数组可以与\(M\)对应起来

现在只需要解释一下如何使用相同前后缀数组进行跳跃,应该就能写出KMP的代码了.

当\(M[5]\)与\(S[5]\)不相同时,看一下前后缀数组\(D\)前一个位置的值,即\(D[4]\)的值为2,那么只需要将\(M[2]\)平移至\(M[5]\)的位置上就可以了.如果\(M[3]\)不匹配,后缀数组\(D\)的前一位\(D[2]\)是0,那么就把\(M[0]\)平移对其\(M[3]\)的位置就可以了.

假如\(M[0]\)不匹配的话,就直接往后跳一位,与\(S\)下一项比较.

KMP代码实现(JAVA)

整体思路就大致如此了,如果理解了应该可以按照思路写出代码了.

下面就贴一下代码实现,我只是简单地测试了一下,没有大量测试,但是整体思路应该大致如此,可能有些边界问题还没有考虑周全,欢迎指正.

	public static int getIndexOf(String s, String m) {
if (s == null || m == null || m.length() < 1 || s.length() < m.length()) return -1;
char[] ss = s.toCharArray(), ms = m.toCharArray();
int si = 0, mi = 0;
int[] next = getMatchArray(ms);
int q = next[mi];
while (si < ss.length && mi < ms.length) {
if (ss[si] == ms[mi]) {
si++;
mi++;
} else {
if (mi != 0) {
mi = next[mi - 1];
} else {
si++;
}
}
}
return mi == ms.length ? si - mi : -1;
} public static int[] getMatchArray(char[] ms) {
if (ms.length == 1) return new int[]{0};
int[] next = new int[ms.length];
next[0] = 0;
int j = 0, tail = 1;
while (tail < ms.length) {
j = next[tail] = ms[j] == ms[tail] ? (j + 1) : 0;
tail++;
}
return next;
} public static void main(String[] args) {
String str = "abcabcababaccc";
String match = "ababa";
System.out.println(getIndexOf(str,match));
}

KMP算法思路的更多相关文章

  1. KMP算法代码

    以下是本人根据上一篇博客随笔http://www.cnblogs.com/jiayouwyhit/p/3251832.html,所写的KMP算法代码(暂未优化),个人认为在基于上一篇博客的基础上,代码 ...

  2. Python 细聊从暴力(BF)字符串匹配算法到 KMP 算法之间的精妙变化

    1. 字符串匹配算法 所谓字符串匹配算法,简单地说就是在一个目标字符串中查找是否存在另一个模式字符串.如在字符串 "ABCDEFG" 中查找是否存在 "EF" ...

  3. KMP算法之next数组的求解思路

    2.next数组的求解思路 本部分内容转自:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algo ...

  4. 简单有效的kmp算法

    以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...

  5. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  6. 数据结构与算法JavaScript (五) 串(经典KMP算法)

    KMP算法和BM算法 KMP是前缀匹配和BM后缀匹配的经典算法,看得出来前缀匹配和后缀匹配的区别就仅仅在于比较的顺序不同 前缀匹配是指:模式串和母串的比较从左到右,模式串的移动也是从 左到右 后缀匹配 ...

  7. 扩展KMP算法

    一 问题定义 给定母串S和子串T,定义n为母串S的长度,m为子串T的长度,suffix[i]为第i个字符开始的母串S的后缀子串,extend[i]为suffix[i]与字串T的最长公共前缀长度.求出所 ...

  8. 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

    之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...

  9. 算法:KMP算法

    算法:KMP排序 算法分析 KMP算法是一种快速的模式匹配算法.KMP是三位大师:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,所以取首字母组成KMP. 少部分图片来自孤~影 ...

随机推荐

  1. accept error: Too many open files

    今天测试socket服务器同一时间处理多个客户端连接问题,第一次测试1000个的时候没问题,第二次测试1000个服务器accept的时候就报错了 accept error: Too many open ...

  2. 面试官:能用JS写一个发布订阅模式吗?

    目录 1 场景引入 2 代码优化 2.1 解决增加粉丝问题 2.2 解决添加作品问题 3 观察者模式 4 经纪人登场 5 发布订阅模式 6 观察者模式和发布订阅模式的对比 什么是发布订阅模式?能手写实 ...

  3. Swift进阶-内存管理

    本文的主要目的是探索 RefCount 的内存结构及强/弱引用计数管理 Swift 中也是采用 ARC 编译器自动内存管理机制. Swift 对象的内存结构是 HeapObject, 有两个属性 Me ...

  4. Python Excel工具类封装, 给excel表头搞点颜色

    封装Excel工具类 我们常用的excel工具类,读有xlrd,写有xlwt.有读有写,新一代库有pandas,openpyxl等等. 大家用法都差不多,今天博主就介绍新手最爱,我也爱的xlrd和xl ...

  5. NCF 中如何将Function升级到FunctionRender

    简介 历史的车轮在不断的向前推进,NCF也在不断的迭代更新,只为成为更好的NCF 如果你之前没有用过NCF可以跳过这个文档,直接去下载最新的NCF源码进行实践. NCF仓库地址:https://git ...

  6. webpack 打包图片资源

    webpack 打包图片资源 /** * loader: 1. 下载 2. 使用(配置) * plugins:1. 下载 2. 引入 3.使用 */ // 用来拼接绝对路径的方法 const {res ...

  7. 一文看懂socket编程

    1.网络模型的设计模式 1.1 B/S模式 B/S: Browser/Server,浏览器/服务器模式,在一端部署服务器,在另外外一端使用默认配置的浏览器即可完成数据的传输. B/S结构是随着互联网的 ...

  8. Python进阶(装饰器)

    from datetime import datetime def log(func):#func表示装饰器作用于的函数 def wrapper(*args,**kw):#wrapper返回装饰器作用 ...

  9. Serverless 下的微服务实践

    作者:弈川 审核&校对:筱姜.潇航 编辑&排版:雯燕 微服务架构介绍 微服务架构诞生背景 在互联网早期即 Web 1.0 的时代,当时流行的是单体应用,研发团队比较小,主要是外部网页, ...

  10. Unity Ioc 类型初始值设定项引发异常,The type name or alias SqlServer could not be resolved. Please check your configuration file and verify this type name.

    先看一下unity的配置信息 <unity> <typeAliases> <typeAlias alias="IDatabase" type=&quo ...