KMP算法分析
来源:互联网 发布:java并发编程面试题 编辑:程序博客网 时间:2024/05/22 08:06
为什么会产生KMP算法?
问题:给定一下字符串S,找出S中匹配W的起始位置?
解决这个问题,一般来讲,可以从S的第0个字符开始,看与W的第0个字符是否相等,如果相等再比较第1个字符,如果相等继续比较,如果不相等,再回到S的第1个字符,与W的第0个字符相比较。
过程如下图所示:
S:ABCFABCDABFABCDABCDABDE
W:ABCDABD
1. 比较发现W的第3个字符与S的第3个字符不相等,中止比较
2. 从S的第1 个字符开始S的第0个字符开始比较,不相等,中止比较
3. 从S的第2个字符开始比较,不相等,中止比较
4. 从S的第3个字符开始比较……
观察图中现象,第1次比较时,S和W的前三个字符是相等的,那么第二次比较等同于W的第0个字符与W的第1个字符比较,第三次比较等同于W的第0个字符与W的第2个字符比较。
再观察第5个,第5步的W的最后一个字符与S的第10个字符不相等,中止比较。但从比较的过程中,可以确定,W的0-5个字符与S的4-9个字符是相等的。W[0…5] == S[4…9]
第5步和第12步,都是在最后一个字符发现不相等,进而中止比较,然后后面的三步的比较过程是惊人的相似!
有没有办法跳过重复的比较过程? 如第12步,可以直接进入到第16步,第4-5的AB与0-1的AB相等,是不是可以直接从W的第3个字符开始比较
上面的描述未免有点一相情愿,缺乏理论根基,Knuth, Morris, Pratt 三个大神已经给出了处理算法:寻找最长首尾匹配位置,该算法对W串进行预处理
算法描述
依据wikipedia中的描述,重新画出KMP算法的处理流程:
其中
S表示待查找的字符串,
W表示模式字符串,
m表示匹配的模式字符串在S中的起始位置,
i表示比较时W字符串的位置指针
第一步,m = 0, S[3]!=W[3]。 W[0]与W[1]、W[2]均不相等, 所以W[0]与S[1]、S[2]均不相等。调整 m = 3, i = 0;
第二步,S[3] != W[0],从S的下一个位置开始比较,m = 4, i = 0
第三步,S[4, 9] = W[0,5], S[10] != W[6]。W[1-3]中均与W[0]不相等,对应的S[5-7]中不会存在与W[0]匹配的字符,而W[0, 1]与W[4,5]相等,下一步比较S[10]与W[2], 此时m = 10 -2 = 8, i = 2;
第四步,m = 8, i = 2,由于W[0]与S[8]、W[1]与S[9]相等,第四步的比较直接从i=2开始,S[10] != W[2],W[0] !=W[1], W[0] !=S[9], 调整 m = 10, i = 0;
第五步,W[0] !=S[10], 从S的下一个字符开始比较,调整 m = 11, i=0
第六步,同第三步,W[6] != S[11 + 6], 而S[15,16] == W[4,5]==W[0,1], 调整m = 15,i = 2;
第七步,找到匹配的W,在S中的起始位置为15。
假设存在局部匹配表T,用于指示当前字符比较失败后下一次比较从哪开始。T的每一项构造满足如下关系: 从S[m]开始比较,当S[m+i]不等于W[i]时,比较从S[m+ i – T[i]]字符串开始。这里有两层暗示:
1. T[0] = -1,即W[0]不匹配的时候,S不需要回溯,需要从下一个字符开始比较。
2. 尽管下一个可能匹配开始的节点起点在 m + i – T[i], 我们并不需要实际check该起点后T[i]个字符,我们继续从W[T[i]]开始搜索。(参见上面第6步描述)。
为什么不需要check前T[i]个字符?
S[m + i] != W[i]中断比较,而前面的字符是相等的,即S[0…m+i-1] == W[0…i-1],在W中 W[0…T[i]-1] == W[i-T[i]…i-1],所以有 S[m+i-T[i]…m+i-1]== W[0…T[i]-1], 进而推出,比较从S[m+i]、W[T[i]]开始。
以上算法 ,可以用于查找匹配的位置,那么下一步需要解决T如何求取。
T[i]表征的含义:当S[m+i]与W[i]不相等时,再次比较的发起位置。
1. W的位置i,前面i个字符中存在最长首尾匹配字符串,假设长度为k,如果k==0表示没有最长首尾匹配串。
2. W[i]与W[k]不相等,则T[i] = k。对于i+1时,需要找到一个新的k,使W[i]与W[k]相等,即找到最长首尾匹配串。由于W[0, k-1] == W[i-k,i-1],且T[k]是可能满足最长首尾匹配串的可能的取值,所以令k = T[k],继续迭代,直到W[k] == W[i]或k< 0;
3. 如果W[i]与W[k]相等,则T[i] = T[k]
举例:
i = 0时,T[0] = -1
i = 1时, k =0, W[1] == W[0], T[1]=T[0]=-1
i = 2时, k =1, W[2] != W[1], T[2] = 1, 未找到以W[2]结尾的最长首尾匹配串,k = -1
i = 3时,k = 0,W[3]!=W[0], T[3] = 0,未找到以W[3]结尾的最长首尾匹配串, k = -1
I = 4时,k =0, W[4] == W[0], T[4] = T[0] = -1
I = 5时,k =1, W[5] == W[1], T[5] = T[1] = -1
I = 6时,k =2, W[6] != W[2], T[6] = 2,未找到以W[6]结尾的最长首尾匹配串,k = -1
I = 7时,k =0……T[7] = 0
I = 8时,k =0……T[8] = 0
I = 9时,k =0, W[9] == W[0], T[9] = T[0] = -1
I = 10时,k =1, W[10] == W[1], T[10] = T[1] = -1
I = 11时,k =2, W[11] == W[2], T[11] = T[2] = 1
I = 12时,k =3, W[12] == W[3], T[12] = T[3] = 0
I = 13时,k =4, W[13] == W[4], T[13] = T[4] = -1
I = 14时,k =5, W[14] == W[5], T[14] = T[5] = -1
I = 15时,k =6, W[15] != W[6], T[15] = 6;
T[6] = 2, W[2] == W[15], 所以k = 2
I = 16时,k =3, W[16] != W[3], T[16]=3,
T[3] = 0, W[0] = -1 != W[16], 未找到k使W[k] == W[i], k = -1;
I = 17时,k = 0
代码实现
public class KMP {private char[] S;private char[] W;private int m;private int i;private int[] T;KMP(char[] S, char[] W) {this.S = S;this.W = W;m = 0;i = 0;T = new int[W.length + 1];}public int find() {generateT();while (m + i < S.length) {while (i < W.length && S[m + i] == W[i]) {i++;}if (i == W.length) {return m;}m = m + i - T[i];i = T[i] < 0 ? 0 : T[i];}return -1;}public void generateT() {T[0] = -1;int cnd = 0; // the zero-based index in W of the next character of the current candidate substringint pos = 1; // the current position we are computing in Tfor (; pos < W.length; cnd++, pos++) {if (W[pos] == W[cnd]) {T[pos] = T[cnd];} else {T[pos] = cnd;// prepare for next poscnd = T[cnd];while(cnd >= 0 && W[cnd] != W[pos]) {cnd = T[cnd];}}}T[pos] = cnd;}public static void main(String[] args) {char[] S = new char[] {'A','B','C',' ','A','B','C','D','A','B', ' ','A','B','C','D','A','B','C','D','A','B','D','E'};char[] W = new char[] {'A','B','C','D','A','B','D'};KMP kmp = new KMP(S, W);System.out.println(kmp.find());}}
参考资料:
https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
http://www.ituring.com.cn/article/59881
- 算法分析 KMP算法
- KMP算法分析
- KMP算法分析
- KMP算法分析
- KMP算法简单分析
- kmp算法原理分析
- KMP算法分析
- kmp算法分析
- KMP算法分析
- kmp算法讲解和分析
- KMP模式匹配算法分析
- KMP算法思考和分析
- KMP算法的效率分析
- 【算法分析】字符串匹配:BF、KMP算法
- KMP模式匹配算法分析与实现
- KMP 模式匹配算法原理分析
- KMP算法之NEXT数组原理分析
- KMP算法之next数组分析
- spring boot和spring cloud 开发必备工具准备 和 必备网站导航
- Shell脚本IF条件判断和判断条件
- Day 4 linux基础命令练习一
- CentOS等Linux系统清理系统垃圾和日志方法
- Springmvc前端控制器配置
- KMP算法分析
- 瞬时频率估计方法
- 淘淘商城系列——使用maven tomcat插件启动聚合工程
- Servlet配置及特性
- HTTP头信息
- View的事件分发机制详解
- imf瞬时频率跳变问题
- 冰雕
- 数据结构-----Trie树