1.朴素的模式匹配算法
将主串的每一个字符作为子串的开头进行匹配,若不相同即不匹配,则将重新匹配子串的下一个字符。
此时我们假设将主串S和要匹配的子串T的长度存在S[0]和T[0]中,实现代码如下:
/*返回子串T在主串S中第pos个字符之后的位置,若不存在,则函数的返回值为0*/
/*T非空,则1<=pos<=StrLength(s)。*/
int index(String S,String T,int pos)
{
int i = pos;/*i用于当pos不为1时,从主串的当前位置下标开始匹配*/
int j = 1;//j用于子串的中的位置下标
while(i<=S[0] && j<=T[0])
{
if(S[i] == T[j])
{
++i;
++j;
} //若相等则继续
else
{
i = i-j+2//此刻是不相等则将指针返回到匹配相等的子串的前一字符,由于下标从0开始所以再加上2则是从不相等的下一字符开始重新匹配
j = 1//j退回到子串的首位
}
if(j > T[0])
{
return i-T[0];
}
return 0;
}
}
由于每次如果主串子串不匹配的话,主串就要移到下一位字符与子串匹配,因此过程比较复杂,时间复杂度位O(m+n),其中m为主串长度,m为子串长度需要重复遍历有时候会很麻烦
故在下面介绍一种效率更高,时间复杂度更低的算法
2.KMP模式匹配算法
KMP算法的核心思想也就是相对于朴素算法的改进在于我们确认了相等字符后的字符与与之对应的主串的字符相等时,我们便不必要将主串的字符再一次遍历与子串作对比 举个例子就像:T[1] = a,T[2] = b,S[2] = b,显然T[1]不等于T[2],T[2] = S[2],因此在子串的j进行到2的时候根本不需要再一次进行遍历,这一步明显是可以进行省略的,在已经知道T串中的一个字符跟后面字符都不同时,我们便不需要再将主串字符向后遍历。
所以关键就在于比较子串字符和后面字符的相似程度,而我们的主串的i并不需要进行操作或移动,只需要总结子串中j的规律便可。
于是我们可以将子串T中的各个位置的j值变化开一个数组next,那么next的长度就是子串的长度,于是可以得到相关的函数为:
0,当j等于1时
next[j] = MAX(后面会总结)
1,其他情况
在这里我们可以举个例子来推导一下next数组的数组值
模式串T = abcabx (j = 123456)
1.当j = 1时,next[1] = 0
2.当j = 2时,next[2] = 1
3.当j = 3时,next[3] = 1
4.当j = 4时,next[4] = 1
5.当j = 5时,由此前缀字符‘a’与后缀字符‘a’相等,由公式(‘P1…Pj-k+1’ = Pj-k+1…Pj-1得到P1 = P4)因此next[5] = 2
6.当j = 6时,同理可推得next[6] = 3
故规律可推为字符有n个相等k值就是n+1
代码如下:
/*通过计算返回子串T的next数组*/
void get_next(String T,int *next)
{
int i , j;
i = 1;
j = 0;
next[1] = 0;
while(i < T[0])//T[0]表示串的长度
{
if(j == 0 || T[i] == T[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];//将串后继继续比较,i值不变
}
if(j>T[0])
return i-T[0];
else
return 0;
}