【算法】KMP算法
简介
KMP算法由 Knuth-Morris-Pratt 三位科学家提出,可用于在一个 文本串 中寻找某 模式串 存在的位置。
本算法可以有效降低在一个 文本串 中寻找某 模式串 过程的时间复杂度。(如果采取朴素的想法则复杂度是 \(O(MN)\) )
这里朴素的想法指的是枚举
文本串的起点,然后让模式串从第一位开始一个个地检查是否配对,如果不配对则继续枚举起点。
前置知识
真前缀
指字符串左部的任意子串(不包含自身),如 abcde 中的 a,ab,abc,abcd 都是真前缀但 abcde 不是。
真后缀
指字符串右部的任意子串(不包含自身),如 abcde 中的 e,de,cde,bcde 都是真后缀但 abcde 不是。
前缀函数
一个字符串中最长的、相等的真前缀与真后缀的长度, 如AABBAAA对应的前缀函数值是 \(2\) 。
原理
注意:在分析的时候,我们规定字符串的下标从 \(1\) 开始。
开始:
我们记扫描模式串的指针为j,而扫描文本串的指针为i,假设一开始i,j都在起点,然后让它们一直下去直到完全匹配或者失配,比如:
j
ABCD
i
ABCDEFG
然后
j
ABCD
i
ABCDEFG
最后在此完成了一次匹配,类似地如果ABCD改为ABCC则在此失配。
j
ABCD
i
ABCDEFG
i,j运作模式如上。
KMP算法就是,当模式串和文本串失配的时候,j指针从真后缀的末尾跳到真前缀的末尾,然后从真前缀后一位开始继续匹配。(从而起到减少配对次数,这便是KMP算法的核心原理)
结合例子解释:
模式串: \(AABBAAA\)
文本串: \(AABBAABBAAA\)
j指针在最后一个A处失配。
j
AABBAAA
i
AABBAABBAAA
因为此时 以j为尾的前缀 所对应的前缀函数值是 \(2\) ,所以 j指针 跳到这里:
j
AABBAAA
i
AABBAABBAAA
然后从下一位开始继续配对:
j
AABBAAA
i
AABBAABBAAA
最后
j
AABBAAA
i
AABBAABBAAA
可以看出,KMP能够有效减少配对次数。
实现
我们记
模式串为p,文本串为s。
从上面的模拟中,我们发现需要预处理出一个数组(记之为next[]),它储存模式串中前缀对应的前缀函数\(\pi()\),如对于字符串ABCABC :
\(\pi(0)=0\) (因为什么都没有)
\(\pi(1)=0\) (A甚至没有真前缀和真后缀)
\(\pi(2)=0\) (AB)
\(\pi(3)=0\) (ABC)
\(\pi(4)=1\) (ABCA)
\(\pi(5)=2\) (ABCAB)
\(\pi(6)=3\) (ABCABC)
同样地,我们发现如果用暴力朴素的想法来统计复杂度是 O(N^2) 不好,于是采用类似于上面的方法,只不过模式串配对的对象是自己罢了。
可以结合代码理解,并注意举例,尝试在纸上模拟这个过程。
for(int i=2,j=0;i<=lenp;i++){
while(j && p[j+1]!=p[i]) j=next_[j]; // 如果j指向元素的下一个元素会和当前配对位置失配,则j跳回去
if(p[j+1]==p[i]) j++; //如果能够配对上,j++
next_[i]=j; //记录当前位置的前缀函数π
}
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char p[N],s[N];
int next_[N];
int main(){
cin>>s+1>>p+1;
int lenp=strlen(p+1),lens=strlen(s+1);
// build next array
for(int i=2,j=0;i<=lenp;i++){
while(j && p[j+1]!=p[i]) j=next_[j]; // 如果j指向元素的下一个元素会和当前配对位置失配,则j跳回去
if(p[j+1]==p[i]) j++; //如果能够配对上,j++
next_[i]=j; //记录当前位置的前缀函数π
}
for(int i=1,j=0;i<=lens;i++){
while(j && p[j+1]!=s[i]) j=next_[j];
if(p[j+1]==s[i]) j++;
// if match
if(j==lenp){
j=next_[j];
cout<<i-lenp+1<<endl;
}
}
for(int i=1;i<=lenp;i++) cout<<next_[i]<<' ';
cout<<endl;
return 0;
}
复杂度
\(O(N+M)\)
【算法】KMP算法的更多相关文章
- 数据结构与算法--KMP算法查找子字符串
数据结构与算法--KMP算法查找子字符串 部分内容和图片来自这三篇文章: 这篇文章.这篇文章.还有这篇他们写得非常棒.结合他们的解释和自己的理解,完成了本文. 上一节介绍了暴力法查找子字符串,同时也发 ...
- 经典算法 KMP算法详解
内容: 1.问题引入 2.暴力求解方法 3.优化方法 4.KMP算法 1.问题引入 原始问题: 对于一个字符串 str (长度为N)和另一个字符串 match (长度为M),如果 match 是 st ...
- 笔记-算法-KMP算法
笔记-算法-KMP算法 1. KMP算法 KMP算法是一种改进的字符串匹配算法,KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.具体实现就是实现一 ...
- 值得花费一周研究的算法 -- KMP算法(indexOf)
KMP算法是由三个科学家(kmp分别是他们名字的首字母)创造出来的一种字符串匹配算法. 所解决的问题: 求文本字符串text内寻找第一次出现字符串s的下标,若未出现返回-1. 例如 text : &q ...
- [C++] [算法] KMP算法
KMP串匹配算法是一个经典的算法. 传统BF算法是传统的字符串匹配算法.很好理解.叶实现.但时间复杂度太高. 本文将从字符串模式字符串被称为.为了匹配字符串被称为主弦. KMP配时能够少移动从串的位置 ...
- 程序员必会算法-KMP算法
KMP算法是一种优秀的字符串匹配算法,字符串匹配的常规算法是一步一步进行移位和比较操作,直至找到完全相匹配的字符串. 下面通过一个例子,为大家仔细说明KMP算法的使用和思路: 问题: 在字符串“DEA ...
- 算法 kmp算法
kmp算法是改进后的字符匹配算法,它与bf算法的区别是,每次从串与主串匹配失败后,从串与主串匹配的位置不同. 下面具体说下这两种算法的区别: 主串:BABCDABABCDABCED 从串:ABCDAB ...
- BF算法 + KMP算法
准备: 字符串比大小:比的就是字符串里每个字符的ASCII码的大小.(其实这样的比较没有多大的意义,我们关心的是字符串是否相等,即匹配等) 字符串的存储结构:同线性表(顺序存储+链式存储) 顺序存储结 ...
- 图解算法——KMP算法
KMP算法 解决的是包,含问题. Str1中是否包含str2,如果包含,则返回子串开始位置.否则返回-1. 示例1: Str1:abcd123def Str2:123d 暴力法: 从str1的第一个字 ...
- 字符串匹配算法——KMP算法
处理字符串的过程中,难免会遇到字符匹配的问题.常用的字符匹配方法 1. 朴素模式匹配算法(Brute-Force算法) 求子串位置的定位函数Index( S, T, pos). 模式匹配:子串的定位操 ...
随机推荐
- Devexpress DockManager多页面浮动窗口会关闭所有页面的问题
注册 DockManager 的 ClosingPanel 事件 private void DockManager1_ClosingPanel(object sender, DockPanelCanc ...
- eNSP启动设备AR1失败记一次解决步骤
eNSP稳定版本下载: 微信搜索公众号"疯刘小三" 关注后回复ensp即可获得下载链接地址 eNSP V100R002C00B510 Setup.exe 最近在用eNSp的时候 ...
- cisco交换机路由器静态路由配置
一.切换模式 router>en //用户模式enable router#conf t //特权模式 ...
- 开源AwaitableCompletionSource,用于取代TaskCompletionSource
1 TaskCompletionSource介绍 TaskCompletionSource提供创建未绑定到委托的任务,任务的状态由TaskCompletionSource上的方法显式控制,以支持未来的 ...
- 入 Go 必读:大型Go工程的项目结构及实战思考 原创 毛剑 QCon 今天
入 Go 必读:大型Go工程的项目结构及实战思考 原创 毛剑 QCon 今天
- K8s 一、(1、容器基本概念 2、k8s基本概念 )
1.容器基本概念 容器其实就是一种特殊的进程,容器是一个 '单进程'模型. Namespace :隔离 Namespace 技术实际上修改了应用进程看待整个计算机"视图",即它的& ...
- servelet 实现Post接口访问
先上代码: package com.jovtec.galaxy.mailbox; import java.io.BufferedReader; import java.io.IOException; ...
- zabbix错收告警
这种情况一般出现在重新调整host或者group导致action里的condition发生变化.此时如果一直不能恢复,可尝试将action disable在enable.
- CCF-命令行选项(模拟)
命令行选项 问题描述 请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项.每个命令行由若干个字符串组成,它们之间恰好由一个空格分隔.这些字符串中的第一个为该命令行工具的名字,由小写字母组 ...
- NodeRED - 全局变量的使用笔记
NodeRED - 全局变量的使用笔记 global global.get(..) :获取全局范围的上下文属性 global.set(..) :设置全局范围的上下文属性 global.keys(..) ...