一、简单的模式匹配算法(BF算法)
在实际应用中我们常常能用到类似串的模式匹配,也称子串的定位操作。例如单词查找,百度搜索都是串的模式匹配操作。
字符串模式匹配: 在主串中找到与模式串相同的?串,并返回其所在位置。
- ?串——主串的?部分,?定存在;
- 模式串——不?定能在主串中找到。
例如:主串为:‘abaabaabcabaa’,模式串:‘baabc’;算出?串第?个字符的位置。
【逻辑分析】: 算出主串中有多少个子串, 模式串与子串进行匹配;子串与模式串从第一个开始匹配,当他们的第一个相等,就接着匹配下一个,知道模式串匹配成功或者失败,最后返回结果。 子串:{abaab,baaba,aabaa,abaab,baabc,aabca,bcaba,cabaa}; 模式串:baabc ;
易知:当匹配到第5个子串时,匹配成功,返回子串第一个字符的位置。在循环匹配中,模式串始终是从第一个字符开始匹配;而主串是当子串匹配不成功时+1,再匹配的。
在很多场景中,主串远比模式串长得多,即n>>m。 匹配成功的最好时间复杂度: O(m) ; 匹配失败的最好时间复杂度: O(n-m+1) = O(n-m)=O(n); 匹配失败的最坏时间复杂度:O(n*m)。
int Index(SString S, SString T, int pos)
{
int i = pos;
int j = 1;
while(i<=S.length && j<=T.length)
{
if(S[i]==T[j])
{
++i;
++j;
}
else
{
i=i-j+2;
j=1;
}
}
if (j>T.length)
return i-T.length;
else
return 0;
}
完整代码:
#include<cstring>
#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
#define MAXSTRLEN 255
typedef char SString[MAXSTRLEN+1];
Status StrAssign(SString T, char *chars) {
int i;
if (strlen(chars) > MAXSTRLEN)
return ERROR;
else {
T[0] = strlen(chars);
for (i = 1; i <= T[0]; i++)
T[i] = *(chars + i - 1);
return OK;
}
}
int Index(SString S, SString T, int pos)
{
int i = pos;
int j = 1;
while(i <= S[0]&&j <= T[0])
{
if(S[i]==T[j])
{
++i;
++j;
}
else
{
i=i-j+2;
j=1;
}
}
if (j > T[0])
return i - T[0];
else
return 0;
return 0;
}
int main()
{
SString S;
StrAssign(S,"abaabaabcabaa") ;
SString T;
StrAssign(T,"baabc") ;
cout<<"主串和子串在第"<<Index(S,T,1)<<"个字符处首次匹配\n";
return 0;
}
二、KMP算法
上面的BF算法中频繁的重复比较相当于模式串在不断地进行自我比较,导致其效率低下。我们不得不思考有没有更高校的方法。
我们不想要主串回溯操作,利用部分匹配直接跳转到还没进行匹配的字符那里。
next数组: 当模式串的第j个字符匹配失败时,令模式串跳到next[j]再继续匹配。
当子串和模式串不匹配时,主串指针i不回溯,模式串指针j=next[j]。
KMP算法,最坏时间复杂度 O(m+n);其中,求 next 数组时间复杂度 O(m),模式匹配过程最坏时间复杂度 O(n)。
#include<cstring>
#include<iostream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
#define MAXSTRLEN 255
typedef char SString[MAXSTRLEN+1];
Status StrAssign(SString T, char *chars) {
int i;
if (strlen(chars) > MAXSTRLEN)
return ERROR;
else {
T[0] = strlen(chars);
for (i = 1; i <= T[0]; i++)
T[i] = *(chars + i - 1);
return OK;
}
}
void get_next(SString T, int next[])
{
int i = 1, j = 0;
next[1] = 0;
while (i < T[0])
if (j == 0 || T[i] == T[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];
}
int Index_KMP(SString S, SString T, int pos, int next[])
{
int i = pos, j = 1;
while (i <= S[0] && j <= T[0])
if (j == 0 || S[i] == T[j])
{
++i;
++j;
}
else
j = next[j];
if (j > T[0])
return i - T[0];
else
return 0;
}
int main()
{
SString S;
StrAssign(S,"aaabbaba") ;
SString T;
StrAssign(T,"abb") ;
int *p = new int[T[0]+1];
get_next(T,p);
cout<<"主串和子串在第"<<Index_KMP(S,T,1,p)<<"个字符处首次匹配\n";
return 0;
}
|