串的匹配算法:暴力匹配与KMP
一.暴力匹配算法
int Index(SString S, SString T){ int i=1,j=1; while(i<=S.length&&j<=T.length){ if(S.ch[i]==T.ch[j]){ ++i,++j; } } else{ i=i-j+2;j=1; } } if(j>T.length) return i-T.length; else return 0; }
分别用计数指针i和j指示主串S和模式串T当中比较的字符位置。从主串S的第一个字符起,与模式T的第一个字符比较,若相等,则继续逐个比较后续字符,否则从主串的下一个字符开始,重新进行匹配,直到模式T中的每个字符依次和主串S中的一个连续的字符序列相等,则称匹配成功。
暴力匹配算法的最坏时间复杂度为O(mn),其中n和m分别为主串和模式串的长度。
二.KMP算法
在暴力匹配中,每次匹配失败将导致主串S比较字符位置的回溯,降低了效率的根源。
字符串的前缀和后缀
前缀:除最后一个字符外,字符串的所有头部子串。
后缀:除第一个字符外,字符串的所有尾部子串。
部分匹配值则为字符串的前后缀的最长相等前后缀长度。
可将部分匹配值写成数组形式,得到部分匹配值(PM)表。
当匹配失败时,移动位数=已匹配字符数-对应的部分匹配值。因为当匹配失败时,部分匹配值的部分无需再比较,用子串前缀后面的元素与主串匹配失败的元素比较即可。
已知:右移位数=已匹配的字符数-对应的部分匹配值,可写为:Move=(j-1)-PM[j-1]
将PM表右移一位,得到next数组。
上式改写成Move=(j-1)-next[j],相当于将子串的比较指针j回退到j=j-Move=j-(j-1)+next[j]=next[j]+1。
求next值的程序:
void get_next(String T int next[]){ int i=1,j=0; next[1]=0; while(i<T.length){ if(j==0||T.ch[i]==T.ch[j]){ ++i;++j; next[i]=j; } else{ j=next[j]; } }
设主串为s1s2....sn 匹配串为p1p2pm,假设此时应与模式中第k个字符继续比较,则模式中前k-1个字符的子串必须满足下列条件,且不可存在k'>k满足下列条件:p1p2...pk-1=pj-k+1pj-k+2...pj-1,若存在满足上述条件的子串,则发生失配时,仅需要将模式向右滑动至第k个字符。
KMP匹配算法
int Index_KMP(String S,String T,int next[]){ int i=1,j=1; while(i<=S.length&&j<=T.length){ if(j==0||S.ch[i]==T.ch[j]){ ++i;++j; } else{ j=next[j]; } if(j>T.length) return i-T.length; else return 0; }
KMP算法的时间复杂度为O(m+n),其主要优点是不回溯。