字符串匹配问题。
一个长度为N的字符串T,模版是一个长度为M(M <= N)的字符串P,求出所有匹配点。
【朴素算法】 好像叫暴风(BF)算法。时间复杂度最坏是 O(M(N-1)) 。可以参考一下
void Simple(char *c, char *d) {
int m = strlen(c), n = strlen(d);
for(int i=0; i<=m-n; i++) {
for(int j=0; j<n; j++) {
if(c[i+j] != d[j]) break;
if(j == n-1) printf("%d\n", i);
}
}
}
KMP算法(Kruth-Morris-Pratt)的效率很高,时间复杂度为 O(M+N),但是不易理解.
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define IND 1 //这是下标从1开始
#define NEXT 1 //输出next数组
void GetFail(char *s, int *next) { //失配函数
int m = strlen(s);
next[0] = next[1] = 0; //重要递推边界
for(int i=1; i<m; i++) {
int j = next[i]; //这是上个推好的next
while(j && s[i] != s[j]) j = next[j]; //顺着之前的
next[i+1] = s[i] == s[j] ? j+1 : 0; //递推
}
}
void Find(char *A, char *T, int *next) { //串、模版、next数组
int n = strlen(A), m = strlen(T);
GetFail(T, next);
int j = 0; //模版T指针
for(int i=0; i<n; i++) { //文本串指针
while(j && T[j] != A[i]) j = next[j]; //失配
if(T[j] == A[i]) j ++; //匹配长度+1
if(j == m) printf("%d\n", i-m+1+IND); //找到了
}
}
char A[1000010], T[1000010];
int next[1000010], len;
int main() {
scanf("%s%s", A, T);
Find(A, T, next);
len = strlen(T);
if(!NEXT) return 0;
for(int i=0; i<len; i++)
printf("%d ", next[i+IND]);
printf("\n");
return 0;
}
KMP算法的精髓在于失配函数。自己匹配自己是算法核心。即如果有个模版是'abcdefghijkl',那KMP算法的优越性就无法体现(next数组都是0)。next数组可以理解为next[i]表示:如果匹配到i发现不对,应该接着匹配哪里(0就是从头匹配)。整个next数组构成了状态转移图,代码中的while循环就是顺着失配边不断转移,直到匹配。
接下来准备学习AC自动机.