终于还是踏上了算法这条不归路,记录一下做题的历程
KMP算法
用以解决串匹配问题,蛮力算法的缺点在于每次匹配失败后向后移动一个字符继续匹配继续匹配。尽管第一次匹配是有必要的,但后续的大量重复匹配就没有必要了。
蛮力算法,eg:
kmp算法的大致过程:
next表的建立是基于模式串中已匹配的串中相似的前缀和后缀,简单的说,就是已匹配前缀中的前缀和后缀有相同部分。大致过程如下(以上图为例):
注:若前缀中含有多个相似的前缀和后缀,kmp会选择右移最小的那个(相对安全),同时也表明舍弃的那部分对齐位置确实是不值得对齐的这一不变性。
一般地,会将next [ 0 ]设置为-1,可以理解为在字符串秩为-1处逻辑上存在一个假想的哨兵 (实际上不存在)—— " * " 通配符,用以匹配所有的子串,来满足第一次的匹配(使j > 0),而使j>0的原因是要避免已匹配串中相似前缀和后缀为空集的情况。如下图
next表构建算法:
int * buildNext(char * P){ // 构造模式串P的next[]表
size_t m = strlen(P), j = 0; // “主”串指针
int * N = new int[m]; // next[]表
int t = N[0] = -1; // 模式串指针( p[-1] 通配符)
while(j < m - 1){
if(0 > t || P[j] == P[t]){ // 匹配
N[++j] = ++t;
}
else{ // 失配
t = N[t];
}
}
return N;
}
kmp主算法:
int match(char * P, char * T){
int * next = buildNext(P); // 构造next表
int n = (int) strlen(T), i = 0; // 文本串指针
int m = (int) srtlen(P), j = 0; // 模式串指针
while(j < m && i < n){ // 自左向右,逐个比对字符
if(0 > j || T[i] == P[j]){ // 若匹配
i++; // 携手共进
j++;
}
else{ // 否则,P右移,T不回退
j = next[j];
}
}
delete [] next; // 释放next表
return i - j;
}
习题
https://leetcode-cn.com/problems/implement-strstr/
本文参考《数据结构》| 邓俊辉