标签:lin with 前言 怎样 方式 main amp log col
前言:上课的时候感觉学生在理解这个算法时有些困难,不知道是因为他们还太小还是因为别的什么,反正我是讲的很清楚了。不管怎样,在这里记录一下吧。算法并不是很难,只是会让初学者感觉有点绕,其实并不是算法绕,而是匹配这个问题本身绕。
假设你对简单的字符串模式匹配过程是理解的。
0、KMP算法简介
要解决的问题是:找出字符串T在字符串S中的位置。
主要思路:当在比较过程中出现失配字符时,主串下标不回溯,模式串根据当前已经匹配的字符串把下标回退到位置k,然后继续下一轮的匹配。
正如来自wikipedia的定义:
In computer science, the Knuth–Morris–Pratt string searching algorithm (or KMP algorithm) searches for occurrences of a "word"
W
within a main "text string"S
by employing the observation that when a mismatch occurs, the word itself embodies sufficient information to determine where the next match could begin, thus bypassing re-examination of previously matched characters
1、相关定义
2、算法描述
//在开始之前,需要清楚的是:当出现以下两种情况任意一种时,i 会后移: //情况一:与模式串第一个字符失配;情况二:与模式串中当前字符匹配 输入:模式串,主串 输出:模式串第一次在主串中出现的位置 (1)初始化:i=0,j=0 (2)判断 S[i]==T[j] 是否成立,若成立,i和j同时后移一位,若不成立,即出现了失配字符,转步骤(3); (3)此时分如下几种情况分析: 若j==0, i后移; 若j>0,则需要把j前移。方法是:首先,找T(0:j-1)最长的相同真前缀和真后缀, 若找到,j的值就是这个满足条件的真前缀后面那个字符的下标; 若没找到,j的值为0,即回到T首。 (4)开始下一轮匹配,重复步骤(2),直到S或T的所有字符都参与比较。
上述步骤中的难点就是步骤(3)。
这一步要解决的问题是当失配的时候,应该把模式串的下标回退到哪里。而再看步骤(3)不难发现,回退的位置只跟T自身有关系。
那么我们可以先准备好一个表格,里面记录在下标 j 处失配时,应该回退到k处继续进行下一轮匹配。这个表格就是next数组。
如:
模式串T: "abcadeabca"
next[]: -1 0 0 0 1 0 0 1 2 3
注:其中,next[0]=-1主要是对应上文中提到的主串下标后移的第一种情况,即匹配过程中在模式串首失配,显然这时T的下标应该继续保持在串首。
为了统一使用“i++; j++”的处理方式,这样,主串下标后移一位后,确保模式串下标在第一个(即j=0)。
3、C语言实现
1 int samePreSufLen(char *p,int m) //找出p[0:m]中(最长相同真前缀串和真后缀串)的长度 2 { 3 int temp; 4 int i; 5 temp=m; 6 while(temp>0) 7 { 8 for(i=0;i<temp;i++) 9 if(p[i]!=p[m-temp+1+i]) 10 break; 11 if(i==temp) 12 return temp; 13 temp--; 14 } 15 if(temp==0) 16 return 0; 17 } 18 ////////////////////////////////////////////////// 19 void getNext(char *t,int *next) 20 { 21 int i; 22 for(i=0;i<t.len;i++) 23 { 24 if(i==0) 25 next[i]=-1; //next[0]=-1 26 else if(i==1) 27 next[i]=0; //next[1]=0 28 else 29 next[i]=samePreSufLen(t,i-1); 30 } 31 32 printf("next[]:"); 33 for(i=0;i<t.len;i++) 34 printf("%d ",next[i]); 35 printf("\n"); 36 } 37 //////////////////////////////////////////////////// 38 int StrIndexKMP(char *s,char *t) // KMP算法 39 { 40 int i=0,j=0; 41 int *next; 42 slen=strlen(s); 43 tlen=strlen(t); 44 if(slen < tlen) 45 return -1; //模式匹配不成功 46 next=(int*)malloc(sizeof(int)*tlen); 47 getNext(t,next); 48 while(i<slen && j<tlen) 49 { 50 if(j==-1 || s[i] == t[j]) //继续匹配下一个字符 51 { 52 i++; 53 j++; 54 } 55 else //模式串指针回溯准备下一次匹配 56 j=next[j]; 57 } 58 if (j==tlen) 59 return(i-tlen); //返回匹配的第一个字符的下标 60 else 61 return -1; //模式匹配不成功 62 }
4、一些关于next数组的改进方法
其实,关于next数组还有不少优化方法值得拿来改进算法,比如:
当next[k]==0 且 T[0]==T[k]时,意味着模式串下标从失配字符下标回退到0依然会失配,因此可以在遇到这种情况时,直接把next[k]赋值为-1.
参考文献:
[1] wikipedia: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
[2] http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/
标签:lin with 前言 怎样 方式 main amp log col
原文地址:http://www.cnblogs.com/wxiaoli/p/7609468.html