在进行字符串匹配问题的时候,很容易想到简单的朴素算法也就是BF算法。
//声明:i主串位置,j模式串位置
BF算法思路:(敌动我也动)
(1)在串S和串T中分别设比较的初始下标 i 和 j 并置零。
(2)当S和T中字符均未结束
- 如果S[ i ] == T [ j ],继续比较S和T的下一个字符
- 否则将 j 移动到第一个,i 移动到 i - j + 1 个位置
(3)如果T中的字符比较完毕,则返回匹配的起始下标(i - j);否则返回 -1。
具体代码:
int BF(char S[],char T[]) //字符串匹配(BF算法)
{
int i = 0, j = 0;
while (S[i] != '\0' && T[j] != '\0')
{
if (S[i] == T[j])
{
i++;
j++;
}
else
{
i = i - j + 1; //回溯
j = 0;
}
}
if (T[j] == '\0')
{
return (i - j);
}
else
{
return -1;
}
}
因匹配不成功时存在大量回溯,没有利用已经部分匹配的结果,因此BF算法中时间性能比较低。
因而出现了对BF算法改进的KMP算法。
KMP算法较BF算法改进之处:
在BF算法的基础上,当比较S[ i ]和T[ j ]不同时,只移动模式串,即T[ j ],移动长度为最长前后缀(即next[ j ])。
KMP算法思路:(敌动我不动)
(1)在串S和串T中分别设比较的起始下标 i 和 j
(2)当S或T中的字符未比较完,循环
- 如果S[ i ] == T[ i ],继续比较S和T的下一个字符
- 否则将 j 向右滑动到next[ j ]位置,即j=next[ j ]
- 如果j = -1,则将 i 和 j 分别加1,准备下一趟比较
(3)如果T中所有字符均比较完毕,则返回匹配的起始下标(i - T串长度);否则返回-1。
具体代码:
int KMP(char S[],char T[])
{
int i = 0, j = 0;
int lenS = strlen(S);
int lenT = strlen(T);
int* next = new int[lenT + 1];
getNext(T, next,lenT);
while (i < lenS && j < lenT)
{
if (j==-1||S[i] == T[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == lenT)
{
return (i - j);
}
else
{
return -1;
}
}
next数组的递归求解思路
(1)如果T[ j ]==T[ i ],很明显,我们的next[ i+1 ]就填入新的匹配长度j+1,用代码来写就是:
if (j == -1 || T[i] == T[j])
{
i++;
j++;
next[i] = j;
}
(2)否则(如果T[ j ] != T[ i ]),j则需要进行回溯,回溯到next[ j ],代码:
else
{
j = next[j];
}
下面给出next数组完整求解的代码:
void getNext(char *T, int* next,int len)
{
int j = -1; //j前缀
int i = 0; //i后缀
next[0] = -1;
while (i < len)
{
if (j == -1 || T[i] == T[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}