需解决的问题:从主字符串S中寻找子字符串T出现的第一个位置?如果出现,返回pos,否则,返回-1
一、BF算法
BF算法就是朴素模式匹配算法,即暴力求解,循环遍历。将子字符串的每个字符和主字符串的字符按顺序比较,如果比较不相等时,两个字符串的索引i和j同时回退,i回退到刚才主字符串起始位置的下一位,子字符串回退到首位,这种方法效率比较低。
如下图:当i和j的位置不相等时,则回退。
//BF算法中索引i和索引j都要回溯,所以效率低
int BF(const char* S, const char* T)
{
int i = 0, j = 0;
while (S[i] != '\0' && T[j] != '\0')
{
if (S[i] == T[j])
{
i++;
j++;
}
else
{
i = i - j + 1;//i回溯
j = 0;//j回溯
}
}
if (T[j] == '\0') //由于字串结束,则说明找到
{
return i - j;
}
else//由于主串结束,则说明没有找到
{
return -1;
}
}
二、KMP算法
KMP算法是为了让BF算法中不必要的回退发生,即i不回退,j回退。问题就在于j回退到哪个位置合适呢?这个回退位置通过一个数组next来记录,next数组的长度等于子串的长度。next数组表示的是当前位置之前的子串的最大公共前后缀长度,当两个字符比较不相等时,则j回退到next[j]的位置。
代码如下:
//j=0,next[j]=-1;表示不进行字符比较
//j>0,next[j]=最长公共前后缀长度
int KMP(const char* S, const char* T)
{
int i = 0, j = 0;
int lenS = strlen(S);
int lenT = strlen(T);
while (i<lenS && j<lenT)//不用S[i]和S[j]是防止数组越界,因为j=next[j],next[0]=-1
{
if (j=-1 || S[i] == T[j])//当j=-1时,需要将j+1让j=0
{
i++;
j++;
}
else
{
j = next[j];//j回溯到next[j]
}
}
if (j == lenT)
{
return i-j;
}
else
{
return -1;
}
}
那么问题就转移到如何求出next数组?如图:j表示前缀,i表示后缀。已知A1前缀和A2后缀相等,比较T[i]和T[j]的情况:
(1)T[i]==T[j],则求值的地方则 next[i+1]=j+1;
(2)T[i]!=T[j],因为A1和A2相等,当T[i]和T[j]不等时,则最长子串可能是B1和B3加上后面一位,此时要将j回退到next[j]的位置,此时如果T[j]=T[i],则跳到步骤(1),否则继续回退。代码如下:
void getNext(char* T, int* next)
{
int j = -1; //j前缀
int i = 0;//i后缀
next[0] = -1;
while (i < strlen(T))
{
if (j == -1 || T[i] == T[j])
{
i++;
j++;
next[i] = j;//当T[i] == T[j],则next[i+1]=j+1;就是在刚才的最长公共前后缀长度+1
}
else
{
j = next[j];/如果T[i] != T[j],则j回退
}
}
}