KMP模式匹配算法
来源:互联网 发布:idea java工程 编辑:程序博客网 时间:2024/06/08 15:25
本文将讲解串匹配中的KMP模式匹配算法。
模式匹配
首先,讲解什么是模式匹配。一个子串在主串中的定位操作通常称做串的模式匹配。在串匹配中,一般将主串称为目标串,将子串称为模式串。本文将统一用S表示目标串,T表示模式串,将从目标串S中查找模式串T的过程称为模式匹配。
朴素的模式匹配算法
朴素的模式匹配算法是一种最简单暴力的模式匹配算法。该算法从目标串S的第一个字符开始和模式串T的第一个字符进行比较,如果相等则进一步比较二者的后继字符,否则从目标串的第二个字符开始再重新与模式串T的第一个字符进行比较,以此类推,直到模式串T与目标串S中的一个子串相等,称为匹配成功,返回T在S中的位置;或者S中不存在值与T相等的子串,称匹配失败,返回-1。
简单来说,就是对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,直到匹配成功或者全部遍历完成为止。
下面是朴素模式匹配算法的Java实现
/** * 朴素模式匹配算法 * 返回子串T在主串S中第pos个字符之后的位置。若不存在,则返回-1 */ public int Brute_Force(String S, String T, int pos){ int i = pos; //i用于主串S中当前位置下标 int j = 0; //j用于子串T中当前位置下标值 while(i < S.length() && j < T.length()){ //若i小于S长度且j小于T的长度 if(S.charAt(i) == T.charAt(j)){ //两字母相等则继续 i++; j++; }else{//如果不相等,则主串S中当前位置下标回溯到原先位置的下一位 i = i-j+1; j = 0; //子串当前位置退回到首位 } } if(j >= T.length()) return i-T.length(); else return -1; }
KMP模式匹配算法
朴素的模式匹配算法虽然比较简单,也比较容易理解。但是它的效率是十分低下的,因为需要逐个遍历。因此,D.E.Knuth, J.H.Morris, V.R.Pratt三位前辈共同研究出了KMP模式匹配算法, 朴素模式匹配算法之所以低效,是因为它需要不断的重复遍历主串,而KMP算法则可以大大避免重复遍历的情况。
KMP算法,是不需要对目标串S进行回溯的模式匹配算法。我们发现这种匹配算法的关键在于当出现失配情况时,应能够决定将模式串T中的哪一个字符与目标串S的失配字符进行比较。所以呢,那三位前辈就通过研究发现,使用模式串T中的哪一个字符进行比较,仅仅依赖于模式串T本身,与目标串S无关。
这里就要引出KMP算法的关键所在next数组,next数组的作用就是当出现失配情况S[i] != T[j]时,next[j]就指示使用T中的以next[j]为下标的字符与S[i]进行比较(注意在KMP算法中,i是永远不会进行回溯的)。还需要说明的是当next[j] = -1时,就表示T中的任何字符都不与S[i]进行比较,下一轮比较从T[0]与S[i+1]开始进行。由此可见KMP算法在进行模式匹配之前需要先求出关于模式串T各个位置上的next函数值。即next[j],j = 0,1,2,3,…n-1。
下面将举例说明如何求解next数组
子串T = “ababaaaba”
1) 当j = 0时,next[0] = -1;
2) 当j = 1时,j由0到j-1就只有字符”a”,所以next[1]=1;
3) 当j = 2时,j由0到j-1的串是”ab”,显然”a”和”b”不相等,所以next[2] = 1;
4) 当j = 3时,j由0到j-1的串是”aba”,前缀字符”a”与后缀”a”相等,所以next[3]=2;
5) 当j = 4时,j由0到j-1的串是”abab”,前缀字符”ab”与后缀”ab”相等,所以next[4]=3;
6) 当j = 5时,j由0到j-1的串是”ababa”,前缀字符”aba”与后缀”aba”相等,所以next[5]=4;
7) 当j = 6时,j由0到j-1的串是”ababaa”,前缀字符”ab”与后缀”aa”并不相等,只有”a”相等,所以next[6]=2;
8) 当j = 7时,j由0到j-1的串是”ababaaa”,只有”a”相等,所以next[7]=2;
9) 当j = 8时,j由0到j-1的串是”ababaab”,前缀字符”ab”与后缀”ab”相等,所以next[8]=3;
因此,next = {-1,1,1,2,3,4,2,2,3}。
下面则是KMP模式匹配算法的Java实现
/** * 通过计算返回子串T的next数组 */ public int[] get_next(String T){ int i,j; i = 0; j = -1; int[] next = new int[T.length()]; next[0] = -1; while(i < T.length()-1){ if(j == -1 || T.charAt(i) == T.charAt(j)){ i++; j++; next[i] = j; }else{ j = next[j]; } } return next; } public int KMP(String S, String T, int pos) { int i = pos; //i用于主串S当前位置下标值 int j = 0; //j用于子串T中当前位置下标值 //得到next数组 int[] next = get_next(T); while (i < S.length() && j < T.length()) { if (j == -1 || S.charAt(i) == T.charAt(j)) { i++; j++; } else { //根据next数组的指示j进行回溯,而i永远不会回溯 j = next[j]; } } if (j == T.length()) { return i - j; } else { return -1; } }
KMP算法的核心就是计算子串T的每一个位置之间的字符串的前缀和后缀公共部分的最大长度(不包括字符串本身,否则最大长度始终是字符串本身)。然后,就可以利用该最大公共长度快速和主串S比较。当每次比较到两个字符串的字符不同时,则可以根据最大公共长度将子串T向前移动(已匹配长度-最大公共长度)位,再继续比较下一个位置。这样也就是说,不用将主串S回溯,并且子串T也不用挨个遍历,既然前缀和后缀部分相等,则当出现不同字符时,只需从后缀部分的下一位接着匹配即可。这样就可以省略很多遍历次数,大大提高了算法效率,时间复杂度也由O(n*m)降为O(n+m)。
- 模式匹配---KMP算法
- 模式匹配 KMP算法
- 模式匹配-KMP算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- 模式匹配kmp算法
- 模式匹配算法kmp
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP算法模式匹配
- KMP 模式匹配算法
- IMP-00038: 无法转换为环境字符集句柄
- Python3 连接各类数据库(Mysql、Mssql、Oracle)
- Unity3D 知识点总结
- MySQL LOAD DATA INFILE
- beeline通过HiveServer2访问Hive的配置和操作
- KMP模式匹配算法
- Android 的全新的 Play Widget
- 工业机器人来了_是淘汰还是新生?
- Powershell在内网渗透中的利用
- 【java笔试系列三】ArrayList和LinkList
- 深入 char * ,char ** ,char a[ ] ,char *a[] 内核
- ubuntu14.04系统假死及强制关机后产生的问题解决
- 大数据架构hadoop初体验一
- python及numpy,pandas易混淆的点