150 likes | 296 Views
模式匹配算法的原理及应用. 引言. 在计算机科学领域,串的模式匹配(算法一直都是 研究 焦点之一。在拼写检查、语言翻译、数据压缩、搜索引擎、网络入侵检测、计算机病毒特征码匹配以及 DNA 序列匹配等应用中,都需要进行串匹配。串匹配就是在主串中查找模式串的一个或所有出现。. 1. 朴素的模式匹配算法. 朴素的模式匹配算法的 核心思想 是 : 一旦某个字符匹配失败 , 从头开 始 ( 即从本次匹配起点后一个字符开始 ) .
E N D
引言 • 在计算机科学领域,串的模式匹配(算法一直都是研究焦点之一。在拼写检查、语言翻译、数据压缩、搜索引擎、网络入侵检测、计算机病毒特征码匹配以及DNA序列匹配等应用中,都需要进行串匹配。串匹配就是在主串中查找模式串的一个或所有出现。
1.朴素的模式匹配算法 • 朴素的模式匹配算法的核心思想是:一旦某个字符匹配失败,从头开 始(即从本次匹配起点后一个字符开始 ). • 主串 (S)a b a b c a b c a c b a b 子串 (T)a b c a c (子串又被称为模式串)红色表示当前这趟比较指针所在位置,兰色表示当前这趟比较中匹配的部分 第一趟(详细过程)a b a b c a b c a c b a b a b c a ca b a b c a b c a c b a b ab c a ca ba b c a b c a c b a b a bc a c遇到不匹配的地方时指针回朔,每次移动一位
第二趟a b a b c a b c a c b a b a b c a c第三趟a b a b c ab c a c b a b a b c ac第四趟a b a b c a b c a c b a b a b c a c第五趟a b a b c a b c a c b a b a b c a c第六趟a b a b c a b c a cb a b a b c a c_ 完成匹配,跳出 假如串的长度为m,子串的长度为n的话,那么这个算法在最坏的情况下的时间复杂度为O(m*n) , 结论:朴素的模式匹配效率低,机械化地去重复匹配了已经比较过的内容。 问题:有没有办法降低它的时间复杂度呢? 当然有! ^_^ 1.朴素的模式匹配算法
2.模式匹配算法的改进—KMP算法 • 由D.E.Knuth, J.H.Morris和V.R.Pratt同时发现的改进模式匹配算法,简称为KMP算法。 • 其基本思想是:每当匹配过程中出现字符串比较不等时,不需回溯主串,而是充分利用已经得到的“部分匹配”结果,过滤掉那些多余的比较,将模式串向右“滑动”尽可能远的一段距离后,继续进行比较,从而提高模式匹配的效率。 • KMP算法核心之处在于:主串指针不用回溯,直接前进!不匹配时子串指针回退。 • 该算法的时间复杂度为O (m+n)。
2.模式匹配算法的改进—KMP算法 • 2.1Next的函数的引出: 分析:要想改进模式匹配的算法,必须减少无效比较,让起点自动移到当前认为的最理想的位置,而不是仅仅只移一位.但是,计算机怎么知道要移几位呢? 解决方法:设置一个next数组 那么next的函数到底是什么呢? next[j]表示当模式串中第j个字符与主串某个字符比较失败后,起点应该向后移几位. (j=next(j))
2.模式匹配算法的改进—KMP算法 • 2.2Next的函数的定义和计算: • 求next数组本质上就是一个模式串T和自己进行匹配的过程 。
2.模式匹配算法的改进—KMP算法 • void getnext (SString t, int next[]) • { • int j=1,k=0; • next[1]=0; • while (j <= t.len) • { • if (k==0 || t.date [j] == t.date [k]) • { • j++; k++; • next [j]=k; • } • else k=next [k]; // j不变(不回溯),k回溯 • } • } 时间复杂度为O(n)
2.模式匹配算法的改进—KMP算法 • 2.3主程序 • int kmpindex ( SString s, SString t ) • { • int next [ Maxlen ],i=1,j=1,v; • getnext ( t,next ); • while (i<= s.len && j <= t.len ) • { • if (j==0 || s.date [i] == t.date [j]) • { • i++; j++; • } • else j = next [j]; //避免多余比较,匹配串指针回退到next[j]的位置 • } • if ( j > t.len ) v = i - t.len; // 若找到,返回主串的匹配首址 • else v=-1; //若没找,返回-1 • reture v; • } 时间复杂度位O(m)
主串S a b a b c a b c a c b a b 子串T a b c a c next(j) 0 1 1 1 2 按照KMP算法,我们再来看看前面我们举的例子。第一趟a ba b c a b c a c b a b a bc a c第二趟a b a b c ab c a c b a b a b c ac第三趟a b a b c a b c a cb a b a b c a c _完成匹配,跳出 2.模式匹配算法的改进—KMP算法
我们看下面的序列S: aabaabbaaaaaabT: aaaab next(j) 01234 这样求出来的next串还不一定最优必须找到更优的next数组。 T串中的‘a’和S串中的‘b’失配,而‘a’的next值指的还是‘a’,那同样的比较还是会失配,而这样的比较是多余的,如果事先设定,当T[i]==T[j],那next[i]就设为next[j],在求next值的时候就已经比较了,这样就可以去掉这样的多余的比较。于是稍加改进得到: T: aaaab next(j) 01234 nextval(j) 00004 修正next [j]
源代码:求修正的nextval[j] • void getnextval(SString t, int nextval[]) • { • int j=1,k=0; • nextval[1]=0; • while (j<=t.len) • { • if (k==0|| t.date[j]==t.date[k]) • { • j++;k++; • if (t.date[j]!=t.date[k]) • nextval[j]=k; • else nextval[j]=nextval[k]; //消去多余的比较 • } • else k=nextval[k]; • } • }
KMP算法的C#源代码 • # include <stdio.h> • # include <string.h> • //kmp algorithm • //getNext -------求模式串t[ ]的next[ ]---即滑动位置数组 • int * getNext( int t[ ], int nextVal[ ] ) • { • int i = 1,j = 0; • nextVal[1] = 0; • while( i < t[0] ) • { • if ( j == 0 || t[i] == t[j] ) • { • i++;j++; • if ( t[i] != t[j] ) nextVal[i] = j; • else nextVal[i] = nextVal[j]; • } • else j = nextVal[j]; • } • return nextVal; • }
KMP算法的C#源代码 • //indexKMP ------kmp主程序 • int indexKMP(char s[ ], int t[ ], int next[ ], int pos ) • { • int i = pos; //主串指针 • int j = 1; //模式串指针 • while( i < strlen(s) && j <= t[0] ) //t[0]: t[ ]的长度 • { • if ( j == 0 || s[i] == t[j] ) • { • i++; • j++; • } • else j = next[j]; • } • if (j > t[0] ) • return i-t[0]; • else return -1; //若没找,返回-1 • }