数据结构学习笔记
十、串的匹配
10.1 直接调用strstr
10.2 KMP算法
把正常的O(n*m)变成O(n+m)。
10.2.1 人为计算match[]数组(next[])
计算过程:从两侧找两个相同的子串,有则等于(长度-1);否则都是-1
1、第一位始终是 -1;
2、第二位因为a≠b,所以 -1;
3、第三位同理;
4、第四位可以找到a和a,所以填 0;
5、第五位可以找到ab和ab,所以填 1;
6、第七位可以找到abca和abca,所以填 3;
10.2.2 当匹配失败,怎么回退
求match[]的时候,就保证了绿色段[0…match[p-1]]和紫色段[m…p-1]是相同的字符。
当在p处匹配失败时,为了实现主串指针 S 不回退,我们应该把模式串向后移,移到绿色和紫色重叠。因为前几位已经确保匹配成功了,可以继续从match[p-1]+1的位置出发继续匹配。
从模式串指针P的角度看,就是P回退到match[p-1]+1的位置。
10.2.3 实现match[]的原理
1、假设已知match[j-1],即绿色段和紫色段相同。
2、查看match[j-1]+1和 j 是否相等。
如果相等,说明match[j] ≥ match[j-1] +1
假设相等,那么就可以反过来去推match[j-1]的值,发现现在match[j-1]的值比之前多了。显然这是不可能的。所以可以确定
match[j] = match[j-1] +1
如果不相等,可以继续拆成更小的子列。拿着最前段绿色+?和最后面紫色段+?去比较。下图中应该拿 j 和 match[match[j-1]]+1 作比较。
此过程是递归的循环的。
typedef int Position;
#define NotFound -1
void BuildMatch(char* pattern, int* match){
int i,j;
int m = strlen(pattern);
match[0] = -1;
for(j=1; j<m; j++){
i = match[j-1];
while((i>=0) && (pattern[i+1] != pattern[j]))
i = match[i]; //不相等,一直回退。直到 i等于-1
if(pattern[i+1] == pattern[j])
match[j] = i+1; //match[j] = match[j-1] +1
else match[j] = -1;
}
}
Position KMP(char* string, char* pattern){
int n = strlen(string);
int m = strlen(pattern);
int s, p, *match;
if(n < m) return NotFound;
match = (int *)malloc(sizeof(int) * m);
BuildMatch(pattern, match);
s = p = 0;
while(s<n && p<m){
if(string[s] == pattern[p]) {s++; p++}//匹配
else if(p > 0) p = match[p-1]+1;//不匹配,p要回退,s不动
else s++; //不匹配,且p在0处,就要改s
}
return (p == m) ? (s-m) : NotFound;//如果P走到m,说明完全匹配
}