模式匹配就是在字符串string中寻找模式pat,c语言中有内置函数strstr。但是今天我学了更快的算法(线性时间复杂性!)——————KMP算法。
这个算法是用三个作者的名字命名的,分别是Knuth,Morris和Pratt。
首先要了解失配函数:
f(j) = i当且仅当i<j且p【0】p【1】...p【i】 = p【j-i】p【j-i+1】...p【j】
否则f(j) = -1
也就是寻找部分匹配,j!=0时从失配字符开始和pf(j-1)+1重新开始比较,j==0时从失配字符的下一个字符和p0重新开始比较。
#include<stdio.h>
#include<string.h>
#define max_string_size 100
#define max_pattern_size 100
int failure[max_pattern_size];
char string[max_string_size];
char pat[max_pattern_size];
int pmatch(char *string,char *pat)
{
int i = 0, j = 0;
int lens = strlen(string);
int lenp = strlen(pat);
while (i < lens && j < lenp)
{
if (string[i] == pat[j])
{
i++;
j++;
}
else if (j==0)
{
i++;
}
else
{
j = failure[j - 1] + 1;
}
}
return ((j == lenp)?(i-lenp):-1);
}
void fail(char *pat)
{
int i, j;
int n = strlen(pat);
failure[0] = -1;
for (j = 1; j < n; j++)
{
i = failure[j - 1];
while (pat[j] != pat[i + 1] && i >= 0)
{
i = failure[i];
}
if (pat[j] == pat[i + 1])
{
failure[j] = i + 1;
}
else
{
failure[j] = -1;
}
}
}
int main(void)
{
char *pat = "abcabcacab";
char *string = "abcsbdakbfakabcasjdkabcabsjkabcabcacabsdakcs";
fail(pat);
printf("%d", pmatch(string, pat)); // 28
return 0;
}
这里将失配函数通过fail函数计算一遍,放入failure数组里再在pmatch函数里直接计算。
这里的fail函数使用的是另一种求失配函数值的定义方法,十分巧妙:
-1, j=0
f(j) = f(j-1)的m次+1,m是使p【f(j-1)的k次+1】=p【j】的最小整数k
-1 ,当且仅当k不存在
这样就能在O(strlen(pat))的时间内计算出失配函数,对于上面代码中的pat,失配函数值依次是 -1 -1 -1 0 1 2 3 -1 0 1
其实这种算法就是每次失配时移位,移动位数有讲究,是通过失配函数值和已匹配字符数算的,这样就省下那些一定失配的位的比较,时间复杂度就低了。