什么是KMP算法:

  KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。

先来看看暴力解法:

  假设主串是目标字符串为S,模式串是待匹配的字符串为P。用暴力算法匹配字符串过程中,我们会把S[0] 跟 P[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把S[1] 跟 P[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。时间复杂度O(m*n)

  代码:

 /**
* 暴力解法
* @param s 主串
* @param p 模式串
* @return
*/
private static int indexOf(String s, String p) {
int i = 0;
int sc = i;
int j = 0;
while(sc<s.length()){
if (s.charAt(sc)==p.charAt(j)) {
sc++;
j++;
if (j==p.length()) {
return i;
}
}else {
i++;
sc=i; // 扫描指针以i为起点
j=0; // 恢复为0
}
}
return -1;
}

  而在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。这样主串的指针就不会回溯了,就能保证一次主串的循环就能解决问题。比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠

  

  这里可以看出指针指向的地方匹配失败,而在已经匹配的模式串子串"ABCAB"中,最长的相同的前缀和后缀是"AB",长度为2,所以j要向右移动到位置2,因为有相同的前缀和后缀,那么在移动的过程中,这几个字符肯定是能够匹配成功的,就不用去比较了。由此可以得出结论:当匹配失败时,在已经匹配的模式串子串中,如果最前面的k个字符和j之前的最后k个字符是一样的,那么j要移动到下一个位置k。

  

  然而,如果每次都要计算最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。因为在P的每一个位置都可能发生不匹配,也就是说我们要计算每一个位置j对应的k,所以用一个数组next来保存,next[j] = k,表示当S[i] != P[j]时,j指针的下一个位置k。那到底怎么计算next数组呢?

  当j为0时,如果这时候不匹配,这种情况,j已经在最左边了,不可能再移动了next[0] = -1;那么当j为1的时候,如果不匹配,j指针一定是后移到0位置的,因为它前面也就只有这一个位置了,next[1] = 0;如果p[j]==p[k]或者k<0,next[++j] = ++k,否则,k=next[k]。

  

  代码:

   public static int[] next(String ps) {
int pLength = ps.length();
int[] next = new int[pLength + 1];
char[] p = ps.toCharArray();
next[0] = -1;
if (ps.length() == 1)
return next;
next[1] = 0; int j = 1;
int k = next[j]; //看看位置j的最长匹配前缀在哪里 while (j < pLength) {
//现在要推出next[j+1],检查j和k位置上的关系即可
if (k < 0 || p[j] == p[k]) {
next[++j] = ++k;
} else {
k = next[k];
}
}
return next;
}

  那么完整的代码就是:

public class KMP {

    public static void main(String[] args) {
String src = "babababcbabababb";
int index = indexOf(src, "bababb");
System.out.println("暴力破解法:"+index);
index = indexOf1(src, "bababb");
System.out.println("KMP算法:"+index);
} //O(m+n),求count 总共出现了多少次
private static int indexOf1(String s, String p) {
if (s.length()==0||p.length()==0) {
return -1;
}
if (p.length()>s.length()) {
return -1;
} // int count = 0;
int []next = next(p);
int i = 0;//s位置
int j = 0;//p位置
int sLen = s.length();
int pLen = p.length(); while(i<sLen){
// ①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
// j=-1,因为next[0]=-1,说明p的第一位和i这个位置无法匹配,这时i,j都增加1,i移位,j从0开始
if (j == -1 || s.charAt(i) == p.charAt(j)) {
i++;
j++;
} else {
// ②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
// next[j]即为j所对应的next值
j = next[j];
}
if (j == pLen) {// 匹配成功了
// count++;
// j = next[j];
// 上面两行代码是计数模式字符串总共出现了多少次的
return (i - j);
}
}
return -1;
// return count; // -1
} public static int[] next(String ps){
int pLength = ps.length();
int []next = new int[pLength+1];
char []p = ps.toCharArray();
next[0] = -1;
if (ps.length()==1) {
return next;
}
next[1] = 0; int j = 1;
int k = next[j]; // 看看位置j的最长匹配前缀在哪里 while(j<pLength){
// 现在要推出next[j+1],检查j和k位置上的关系即可
if (k<0||p[j]==p[k]) {
next[++j] = ++k;
}else {
k = next[k];
}
}
return next;
}
/**
* 暴力解法
* @param s 主串
* @param p 模式串
* @return
*/
private static int indexOf(String s, String p) {
int i = 0;
int sc = i;
int j = 0;
while(sc<s.length()){
if (s.charAt(sc)==p.charAt(j)) {
sc++;
j++;
if (j==p.length()) {
return i;
}
}else {
i++;
sc=i; // 扫描指针以i为起点
j=0; // 恢复为0
}
}
return -1;
} }

  结果:

    

  

字符串匹配(二)----KMP算法的更多相关文章

  1. 字符串匹配的kmp算法 及 python实现

    一:背景 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题. Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的常 ...

  2. Luogu 3375 【模板】KMP字符串匹配(KMP算法)

    Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...

  3. 字符串匹配的 KMP算法

    一般字符串匹配过程 KMP算法是字符串匹配算法的一种改进版,一般的字符串匹配算法是:从主串(目标字符串)和模式串(待匹配字符串)的第一个字符开始比较,如果相等则继续匹配下一个字符, 如果不相等则从主串 ...

  4. HDU 1711 Number Sequence (字符串匹配,KMP算法)

    HDU 1711 Number Sequence (字符串匹配,KMP算法) Description Given two sequences of numbers : a1, a2, ...... , ...

  5. 字符串匹配(KMP 算法 含代码)

    主要是针对字符串的匹配算法进行解说 有关字符串的基本知识 传统的串匹配法 模式匹配的一种改进算法KMP算法 网上一比較易懂的解说 小样例 1计算next 2计算nextval 代码 有关字符串的基本知 ...

  6. 实现字符串匹配的KMP算法

    KMP算法是Knuth-Morris-Pratt算法的简称,它主要用于解决在一个长字符串S中匹配一个较短字符串s. 首先我们从整体来把我这个算法的思想. 字符串匹配的朴素算法: 我们容易想到朴素算法, ...

  7. 字符串匹配的KMP算法

    ~~~摘录 来源:阮一峰~~~ 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串”BBC ABCDAB ABCDABCDABDE”,我想知道,里面是否包含另一个字符串”ABCDABD”? 许 ...

  8. 字符串匹配的KMP算法详解及C#实现

    字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...

  9. 字符串匹配与KMP算法实现

    >>字符串匹配问题 字符串匹配问题即在匹配串中寻找模式串是否出现, 首先想到的是使用暴力破解,也就是Brute Force(BF或蛮力搜索) 算法,将匹配串和模式串左对齐,然后从左向右一个 ...

随机推荐

  1. docker镜像常用操作

  2. js 提取字符串中所有的英文

    提取数字....value.replace(/[^\d]/g,'') 提取中文....value.replace(/[^\u4E00-\u9FA5]/g,'') 提取英文.....value.repl ...

  3. win10家庭版升级为专业版(win10专业版激活方法)

    替换专业版密钥 1.在win10家庭版桌面上鼠标右键点击[此电脑]-[属性],点击右下角的[更改产品密钥] 2.也可以点击开始-设置-更新和安全-激活-[更改产品密钥] 3.输入要升级的win10版本 ...

  4. ubuntu配置小飞机

    现在有两种方式在ubuntu配置本地shadowsocks(前提已经在服务器上搭建好了ss) windows,ios和安卓配置都是gui,没什么好说的.然后前期工作什么买vps啊,比较无脑,不想记录这 ...

  5. SourceTree推送分支时遇到ArgumentException encountered错误的解决办法

    下载并安装: https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases/tag/v1.17.2 然后重新推送即可 ...

  6. shiro(四)项目开发中的配置、

    配置拦截.过滤.验证请求 <!-- shiro --> <!-- 項目自定义的Realm --> <bean id="ShiroRealm" clas ...

  7. 今天分享三种方法实现Linux系统调用,感兴趣的朋友可以参考一下

    系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU.磁盘.打印机等)进行交互提供的一组接口.当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系 ...

  8. 使用Tornado异步接入第三方(支付宝)支付

    目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...

  9. C语言--第五次作业--指针

    1.本章学习总结 1.1 思维导图 1.2本章学习体会及代码量学习体会 1.2.1学习体会 没想到都已经学习完C语言的灵魂-指针的内容了(当然也是C里面最难学习的内容了).虽然在之前就有听学习进度比较 ...

  10. Hibernate: '\xE6\x9D\x8E\xE5\x9B\x9B' for column 'cust_name' at row 1 解决

    新建Hibernate,出现异常 20:11:03,117 WARN SqlExceptionHelper:137 - SQL Error: 1366, SQLState: HY000 20:11:0 ...