当前位置: 首页 > news >正文

Knuth-Morris-Pratt Algorithm

KMP背景分析


普通算法(遍历),会遗忘所有之前比较过的信息,导致每一次移位,都要重新重头比较每一个字符。这将会导致 O(mn)的时间复杂度(m: 关键字符长度,n: 文本string的长度)

而KMP算法,则能够保证不去重复比较已经部分匹配的字符,比如序列“ abcd abac”,如果“abcd”部分匹配了文本,而在接下来的“a”位置上不匹配,那么算法则会直接跳过4个位置,重新进行比较,而不是移位1个,从头进行比较。这样就能够保证时间复杂度为 O(n)。说通俗一些就是直接跳到 公共前后缀的位置:

567993-20170119085154921-1617509979.png
图中阴影部分代表公共前后缀,对于“abcdabac”就是“ab”
 

为了保证上述复杂度,需要对关键字符进行预处理(就是标明最长公共前后缀的辅助数组),而这一过程的时间复杂度为 O(m)。因为 m<n,所以总的算法时间复杂度为 O(n)

一些定义


令 x = abacab,那么x的一些术语描述如下:
proper prefixes 前缀:
   a, ab, aba, abac, abaca
proper suffixes 后缀:
   b, ab, cab, acab, bacab
borders 边界(前,后缀共有的最长子串):
   ab
边界 ab 拥有 2 的宽度
而我们的预处理,就是对 关键字字符串 算出每个位置上的 边界。

比如:
j: 0 1 2 3 4 5
p[j]: a b a b a a
b[j]:   0 0 1 2 3 1

预处理过程


该过程就是生成 partial match table 或者称为 failure function的算法,即生成 b[j] 数组。

b[j]的生成过程,其实可以对关键字符串P,用 递推方式 算出来,时间复杂度为 O(m)

假设 b[0], ..., b[i-1] 已知,那么 边界b[i] 的值,通过比较 P j,P i  可以得到
567993-20170119085155343-1018763411.png

如果 P j== P i, 那么如果当前 i-1 位置的边界宽度是j,那么 b[i] = j + 1
如果不相等,那么需要重新获得下一个最大边界长度,这里需要用到如下定理:

567993-20170119085155687-1136905218.png
当字符串x的最大边界是s,次大边界是r,可以推得s的最大边界就是r。
当想扩展x的 边界s 不成功,那么就把x的边界变为s的边界r,重新扩展:

使用如下例子,关键子字符串P:
    j'      j             i i'
a a a b a a e e a a a b a a a ...
|r|                     |r|
|----s----|     |----s----|
|------------P------------|...

当前的位置i的最大边界是s,次大边界是r。假设边界s的宽度是 j = 6。
当i移动到下一个位置i'的时候,“e”和“a”并不相等,边界无法拓展,于是更新 j = b[j-1], 于是j更新为r的宽度2,记为j'
再次比较Pj' 和 Pi'是否相等,发现相等,于是更新b[i'] = b[j'] + 1;同时更新j' = 3;(如果还不相等,就再次找r的最大边界,直到j更新为0)

代码


preprocessing的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
vector< int > kmpProcess(string s) {
         vector< int > b(s.size(),0);
         int  j = 0;
         //下面计算b[i]
         for  ( int  i = 1; i < s.size(); i++) {
             while  (j > 0 && s[i] != s[j]) 
                 j = b[j - 1]; //当前的widest border不满足要求,那么找到next widest border
             if  (s[i] == s[j]) 
                 j++;
             b[i] = j;
         }
         return  b;
     }
 


null


转载于:https://www.cnblogs.com/zhxshseu/p/ef2f76d5d840793202ab216e5f4f62a5.html

相关文章:

  • cas记录
  • spring事物的传播行为及隔离
  • 华为通报 6 名前中高层被抓疑泄露资料给乐视酷派
  • linux cp/scp命令+scp命令详解
  • 在Spring Boot中使用Spring Retry
  • 用C#开发Windows服务监控系统使用
  • 8VC Venture Cup 2017 - Elimination Round - A
  • PostgreSQL SystemTap on Linux
  • Java 操作XML,JDOMDOM4J
  • React怎么创建.babelrc文件
  • JavaSE 学习参考:switch语句
  • Git使用技巧(1)-- 配置【持续更新】
  • Maven 上传 jar 到 私服命令
  • 我们发的不是红包,而关系证明
  • grep和sed匹配多个字符关键字的用法
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • 77. Combinations
  • Akka系列(七):Actor持久化之Akka persistence
  • axios 和 cookie 的那些事
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • linux学习笔记
  • maven工程打包jar以及java jar命令的classpath使用
  • Mysql优化
  • React-Native - 收藏集 - 掘金
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 产品三维模型在线预览
  • 翻译:Hystrix - How To Use
  • 机器学习 vs. 深度学习
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 算法-图和图算法
  • 听说你叫Java(二)–Servlet请求
  • 我的zsh配置, 2019最新方案
  • 我这样减少了26.5M Java内存!
  • 应用生命周期终极 DevOps 工具包
  • 做一名精致的JavaScripter 01:JavaScript简介
  • AI算硅基生命吗,为什么?
  • (HAL库版)freeRTOS移植STMF103
  • (二)pulsar安装在独立的docker中,python测试
  • (二)WCF的Binding模型
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (转)Linq学习笔记
  • (转)visual stdio 书签功能介绍
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .gitattributes 文件
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Core引入性能分析引导优化
  • .Net MVC4 上传大文件,并保存表单
  • .net web项目 调用webService
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)