KMP算法-Java实现
目的:
为了解决字符串模式匹配
历程:
朴素模式匹配:逐次进行比较
KMP算法:利用匹配失败得到的信息,来最大限度的移动模式串,以此来减少比较次数提高性能
概念:
m:是目标串长度
n:是模式串长度
j:某次匹配时,第一次出现的不同的索引位置(有的称为:失配位)
k:最长首尾串长度(有的称为:最长公共前后缀)
核心思想:
S S0 S1 ...... Si-j-1 Si-j Si-j+1 Si-j+2 ...... Si-2 Si-1 Si ...... Sn-1
|| || || || || ×
P P0 P1 P2 Pj-2 Pj-1 Pj
有Si-j-1Si-jSi-j+1 Si-j+2 ...... Si-2 Si-1=P0P1 P2 ...... Pj-2 Pj-1
如果 P0P1 P2 ...... Pj-2 ≠ P1 P2 ...... Pj-2Pj-1
则可以立即断定 P0P1 P2 ...... Pj-2 ≠ Si-j+1 Si-j+2 ...... Si-2 Si-1,即:朴素模式匹配的下一次移动一定不匹配,则可以跳过这一次
如果 P0P1 P2 ...... Pj-3 ≠ P2 ...... Pj-2Pj-1
则可以立即断定 P0P1 P2 ...... Pj-2 ≠ Si-j+1 Si-j+2 ...... Si-2 Si-1,即:朴素模式匹配的下一次移动一定不匹配,则可以跳过这一次
直到第一次出现相等的情况终止:P0P1 P2 ...... Pk-1 = Pj-k ...... Pj-2Pj-1
得到的k就是最长的首尾串长度,然后通过 j-k 得到了我们需要移动的位数,这样我们就利用了匹配失败的结果,得到了我们可以移动的步数,提升了性能
关于k:
其实肉眼就直接能看出来,k是最长首尾串长度,比如:
11111 k=4(前缀:1111,后缀:1111)
12112 k=2(前缀:12,后缀:12)
12345 k=0(无相同前缀后缀)
例子:
S=ababababababb
P=abababb
重申一下原理:朴素模式匹配效率低的原因是一位一位的比较,丢弃了之前失败的信息。而KMP算法从匹配失败的信息中得到可以最大移动的步数,以此来减少比较的次数,来提升性能。
这里并没有提及,next数组及newnext数组,模式串的特征向量N,其实不用管它,思想理解了,只是别人起了个叫法而已。
Java代码:
/**
* 朴素模式匹配
*
* @param source 目标串
* @param pattern 模式串
*/
private static void plain(String source, String pattern) {
int res=0;
int sourceLength=source.length();
int patternLength=pattern.length();
for(int i=0;i<=(sourceLength-patternLength);i++){
res++;
String str=source.substring(i, i+patternLength);
if(str.equals(pattern)){
p("朴素模式:匹配成功");
break;
}
}
p("朴素模式:一共匹配"+res+"次数");
}
//KMP算法实现
private static void KMP(String source, String pattern) {
int[] N=getN(pattern);
int res=0;
int sourceLength=source.length();
int patternLength=pattern.length();
for(int i=0;i<=(sourceLength-patternLength);){
res++;
String str=source.substring(i, i+patternLength);//要比较的字符串
p(str);
int count=getNext(pattern, str,N);
p("移动"+count+"步");
if(count==0){
p("KMP:匹配成功");
break;
}
i=i+count;
}
p("KMP:一共匹配"+res+"次数");
}
/**
* 得到下一次要移动的次数
*
* @param pattern
* @param str
* @param N
* @return 0,字符串匹配;
*/
private static int getNext(String pattern,String str,int[] N) {
int n = pattern.length();
char v1[] = str.toCharArray();
char v2[] = pattern.toCharArray();
int x = 0;
while (n-- != 0) {
if (v1[x] != v2[x]){
if(x==0){
return 1;//如果第一个不相同,移动1步
}
return x-N[x-1];//x:第一次出现不同的索引的位置,即j
}
x++;
}
return 0;
}
private static int[] getN(String pattern) {
char[] pat=pattern.toCharArray();
int j=pattern.length()-1;
int[] N=new int[j+1];
for(int i=j;i>=2;i--){
N[i-1]=getK(i,pat);
}
for(int a:N)
p(a);
return N;
}
private static int getK(int j, char[] pat) {
int x=j-2;
int y=1;
while (x>=0 && compare(pat, 0, x, y, j-1)) {
x--;
y++;
}
return x+1;
}
private static boolean compare(char[] pat,int b1,int e1,int b2,int e2){
int n = e1-b1+1;
while (n-- != 0) {
if (pat[b1] != pat[b2]){
return true;
}
b1++;
b2++;
}
return false;
}
public static void p(Object obj) {
System.out.println(obj);
}
next数组:
KMP能提高性能原因是减少了比较次数,也就是知道k
而k从只和j有关,这就意味着移动的次数只和模式串有关,和目标串无关
简单来说,就是我们得到模式串后就能立马知道移动的次数,这就是next数组。里面储存的就是k值。
KMP算法-Java实现的更多相关文章
- KMP算法java实现
/** * 假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置 如果j = -1,或者当前字符匹配成功(即S[i] == * P[j]),都令i++,j++,继续匹配下一个字符: 如果j != ...
- 经典KMP算法C++与Java实现代码
前言: KMP算法是一种字符串匹配算法,由Knuth,Morris和Pratt同时发现(简称KMP算法).KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.比 ...
- 大话数据结构(十二)java程序——KMP算法及改进的KMP算法实现
1.朴素的模式匹配算法 朴素的模式匹配算法:就是对主串的每个字符作为子串开头,与要连接的字符串进行匹配.对主串做大循环,每个字符开头做T的长度的小循环,直到成功匹配或全部遍历完成为止. 又称BF算法 ...
- Java实现KMP算法
/** * Java实现KMP算法 * * 思想:每当一趟匹配过程中出现字符比较不等,不需要回溯i指针, * 而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远 * 的一段 ...
- [转]KMP算法理解及java实现
这大概是我看的最好懂的KMP算法讲解了,不过我还只弄懂了大概思想,算法实现我到时候用java实现一遍 出处:知乎 https://www.zhihu.com/question/21923021/ans ...
- KMP算法中next数组的理解与算法的实现(java语言)
KMP 算法我们有写好的函数帮我们计算 Next 数组的值和 Nextval 数组的值,但是如果是考试,那就只能自己来手算这两个数组了,这里分享一下我的计算方法吧. 计算前缀 Next[i] 的值: ...
- 算法(Java实现)—— KMP算法
KMP算法 应用场景 字符串匹配问题 有一个字符串str1 = " hello hello llo hhello lloh helo" 一个子串str2 = "hello ...
- Java数据结构之字符串模式匹配算法---KMP算法2
直接接上篇上代码: //KMP算法 public class KMP { // 获取next数组的方法,根据给定的字符串求 public static int[] getNext(String sub ...
- Java数据结构之字符串模式匹配算法---KMP算法
本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基 ...
随机推荐
- Javascript本质第二篇:执行上下文
在上一篇文章<Javascript本质第一篇:核心概念>中,对Javascript执行上下文做了解释,但是这些都是基于Javascript标准中对执行上下文的定义,也就是说理论上的东西,本 ...
- Mongoose Schemas定义中timestamps选项的妙用
在Node.js中使用MongoDB少不了Mongoose. 假设有如下Mongoose Schemas的定义: var ItemSchema = new mongoose.Schema({ biz: ...
- 在JavaScript中,利用三元运算符生成当前日期yyyy-MM-dd
<script type="text/javascript"> //得到当前时间yyyy-MM-dd var myDate = new Date(); var nowD ...
- Atitit sql计划任务与查询优化器--统计信息模块
Atitit sql计划任务与查询优化器--统计信息模块 每一个统计信息的内容都包含以上三部分的内容. 我们依次来分析下,通过这三部分内容SQL Server如何了解该列数据的内容分布的. a.统计信 ...
- ExecuteReader在执行有输出参数的存储过程时拿不到输出参数
异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 后期会在博客首发更新:http://dnt.dkill.net/Article/D ...
- sizzle分析记录:属性选择器
源码部分 通过Sizzle.attr匹配出值 然后通过表达式刷选计算 "ATTR": function( name, operator, check ) { return func ...
- 深入理解定时器系列第二篇——被誉为神器的requestAnimationFrame
× 目录 [1]引入 [2]特点 [3]使用[4]兼容[5]应用 前面的话 与setTimeout和setInterval不同,requestAnimationFrame不需要设置时间间隔.这有什么好 ...
- android表白app
一.前言 马上就要520和521了,是不是还有像我一样的单身狗啊.我就知道有,所以这两天简单写了这个小程序(其实是替别人写的),虽然我并不会用去骗女孩子(因为最近太忙了,实习完之后要搞毕设,要搞论文啊 ...
- lintcode二叉树的锯齿形层次遍历 (双端队列)
题目链接: http://www.lintcode.com/zh-cn/problem/binary-tree-zigzag-level-order-traversal/ 二叉树的锯齿形层次遍历 给出 ...
- Util应用程序框架公共操作类(十):可空值类型扩展
当你使用可空的值类型时,你会发现取值很不方便,比如Guid? obj,你要从obj中获取值,可以使用Value属性obj. Value,但obj可能为null,这时候就会抛出一个异常. 可空值类型提供 ...