KMP(超详细复杂度分析)
从 stackoverflow中找到了一个时间复杂度分析很棒的链接 https://www.inf.hs-flensburg.de/lang/algorithmen/pattern/kmpen.htm
判断字符串 str 中是否包含子串 match。
next [i] : match [i-1] 结尾的后缀子串(不包含match [0])和 match [0] 开头的前缀子串,两者的最大匹配长度。
- 因为match[0] 前面没有字符串,规定 next [0] == -1
- 因为 next [i] 对应的子串不包含 match [0],所以next[1] = 0
假设当前 str[i ... j-1] 和 match [0 ... j-i-1]:若 str [j] != match [j-i],
- 若next [j] != -1,下一个比较的位置不是 str [i+1] 和 match [0],而是 str[j] 和 match [next [j-i]]
- 若next [j] = -1,说明 match 的索引指向 match [0],即 j - i = 0,并且在上一次比较中,match [0] != str [j],此时 str 的索引加1即可。
算法的精髓在于搞清楚一个问题:str [i] 和 str [j] 之间是否存在以 str [j-1] 结尾且长度大于 next[j-i] 的子串呢?
答案显然是否定的,这违反了 next 数组的定义。
时间复杂度:O(N),分析:
先看匹配过程:
- 方法的循环体中有3个分支。
- 循环中,si++发生的次数等于 s.length - 1。因此,进入前2个分支的次数是 s.length - 1。
- 其次,mi回退(match滑动)的过程等价于 match 对应 str 往右至少一个位置,显然它往右(match滑动)的最大次数是 s.length - m.length。因此,进入最后1个分支的次数是s.length - m.length。
- 所以循环发生的次数 2 * s.length - m.length + 1,即2N-M+1。
再看next数组生成:
- 方法的循环体中有3个分支。
- 循环中,pos++发生的次数等于 m.length - 2。因此,进其中2个分支的次数是 m.length - 2。
- 其次,cn回退最多发生多少次,受限制于 ++cn 执行了多少次,++cn 和 pos++ 同时发生最多发生的次数是 m.length - 2。
- 所以循环发生的次数 2 * m.length - 4,即2M-4。
最后看总复杂度:
- (2N-M+1) + (2M-4) = (2N+M-3) = O(2N+M)
- 因为 N >= M,O(2N+M) = O(3N) = O(N)
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();
char[] ms = m.toCharArray();
int si = 0;
int mi = 0;
int[] next = getNextArray(ms);
while (si < ss.length && mi < ms.length) {
if (ss[si] == ms[mi]) {
//匹配
si++;
mi++;
} else if (next[mi] == -1) {
//当前mi = 0,str[si] != match[0],si++即可
si++;
} else {
//滑动
mi = next[mi];
}
}
return mi == ms.length ? si - mi : -1;
}
怎么计算next数组?
- match [0] == -1,match[1] = 0(原因已经给出)。
- 从左至右依次计算,计算 next [i] 时已知 next [0 ... i-1]
- 我们可以利用 next [i - 1],若 match [i-1] = match [next [i-1]],那么 next [i] = next[i-1] + 1(再长的话与next数组定义违背)
- 若 match [i-1] != match [next [i-1]],则比较 match[i-1] 和 match[next[next[i-1]]],原因如下:
- 假设next[i-1] 对应前后缀分别是A和B,那么 next [next [i-1]] 则代表A的前后缀最大匹配长度。
- 由于A=B,因此A的前缀能对应B的后缀。
- 当前可能性不可能大于 next [next [i-1]] + 1,否则与next数组定义违背。
- 第3步和第4步递归执行,直到 next [k] = 0,则令 next [i] = 0。
public static int[] getNextArray(char[] ms) {
if (ms.length == 1) {
return new int[] { -1 };
}
int[] next = new int[ms.length];
next[0] = -1;
next[1] = 0;
//当前将要计算的位置
int pos = 2;
//当前将要被比较的位置
int cn = 0;
while (pos < next.length) {
if (ms[pos - 1] == ms[cn]) {
// cn是位置,长度=位置+1
next[pos++] = ++cn;
//此刻,cn = next[pos - 1]
} else if (cn > 0) {
cn = next[cn];
} else {
next[pos++] = 0;
//此刻,cn = next[pos-1] = 0
}
}
return next;
}
KMP(超详细复杂度分析)的更多相关文章
- 利用 Docker Compose 搭建 SpringBoot 运行环境(超详细步骤和分析)
0.前言 相信点进来看这篇文章的同学们已经对 Docker Dompose 有一定的了解了,下面,我们拿最简单的例子来介绍如何使用 Docker Compose 来管理项目. 本文例子: 一个应用服务 ...
- PHP yield 分析,以及协程的实现,超详细版(上)
参考资料 http://www.laruence.com/2015/05/28/3038.html http://php.net/manual/zh/class.generator.php http: ...
- ArrayList源码分析超详细(转载)
ArrayList源码分析超详细 ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要 ...
- 超强、超详细Redis数据库入门教程
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...
- 超强、超详细Redis数据库入门教程(转载)
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使 ...
- 超强、超详细Redis入门教程【转】
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...
- 超详细Redis入门教程【转】
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使 ...
- c语言面试宝典(经典,超详细)
c语言面试宝典(经典,超详细) 2018年08月25日 09:32:19 chengxuyuan997 阅读数:7799 摘自:https://blog.csdn.net/chengxuyuan9 ...
- SPSS超详细操作:分层回归(hierarchical multiple regression)
SPSS超详细操作:分层回归(hierarchical multiple regression) 1.问题与数据 最大携氧能力(maximal aerobic capacity, VO2max)是评价 ...
随机推荐
- Codeforces Round #660 (Div. 2) Captain Flint and Treasure 拓扑排序(按照出度、入读两边拓扑排序)
题目链接:Captain Flint and Treasure 题意: 一种操作为 选一个下标 使得ans+=a[i] 且 把a[b[i]]+a[i] 要求每个下标都进行一种这样的操作,问怎么样的 ...
- 用servlet在网页中打印字符串(初接触)、servlet调用过程
一.servlet是什么: 二.在官方文档中点servlet 这就是servlet的方法,这里说一下什么叫生命周期的方法(life-cycle methods):就是这个对象一旦创生之后一定会执行的方 ...
- Codeforces Round #646 (Div. 2) C. Game On Leaves (贪心,博弈)
题意:给你一棵树,每次可以去掉叶节点的一条边,Ayush先开始,每回合轮流来,问谁可以第一个把\(x\)点去掉. 题解:首先如果\(x\)的入度为\(1\),就可以直接拿掉,还需要特判一下入度为\(0 ...
- K8S(10)配置中心实战-configmap资源
k8s配置中心实战-configmap资源 目录 k8s配置中心实战-configmap资源 0 configmap前置说明 0.1.1 configmap和secret 0.1.2 怎么使用conf ...
- Linux错误记录贴
add-apt-repository 不要写成 add-apt-repository service 不要写成 sevice 总之在打命令的时候要注意不要拼错单词 对于ls命令权限不够我们可以先su ...
- 攻防世界-Web-lottery(.git泄露、php源码审计、弱类型利用)
扫描目录,发现.git泄露: 提取.git泄露的源码,得到许多文件: 网站这里: 这就要审计一下代码,找找漏洞了. 经过一番审计,猜数字对应的函数在api.php中: 我们要绕过这个$win_numb ...
- 用python写的一个自动卸载python包的脚本
import osplist=os.popen("pip list") # 执行windows cmd命令,获取所有包package列表,并获取返回结果到plist#跳过第1,2行 ...
- Object Destructuring Assignment vs Object.assign
Object Destructuring Assignment vs Object.assign // const params = Object.assign({}, this.$route.par ...
- auto deploy docs website
auto deploy docs website { "name": "docs", "version": "0.0.1" ...
- free online markdown editor
free online markdown editor markdown https://blog.csdn.net/xgqfrms/article/details/50129317 In-brows ...