假设字符串A: "abcababcabc"
字符串B:"abcabc"
因为字符串A中有一部分和B相同,则称字符串B为字符串A的子串
用i 和 j 分别表示字符串AB的下标,初始值都为0。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
1、首先字符串A从头开始,判断往后strlen(B)个字符串是否和字符串B相等;
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
2、当走到下标为5的时候,字符串A B失配,则说明字符串A从下标为0开始的子串与B不相同,既然从下标为0开始没有,那就i回退到1(即回退了i - j + 1),j回退到0,再次进行匹配
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
3、当i为1,j为0是还是失配,i接着回退i - j + 1 = 2,j接着回退到0,接着匹配
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
4、重复上述步骤,直到:i = 5,j = 0的时候
用i 和 j 分别表示字符串AB的下标,初始值都为0。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
此时,可以直到 j 走到字符串B末尾,字符串A B都匹配,那么说明从 i 开始存在子串B。
5、当i走到字符串A末尾都没有出现j走到字符串B末尾的情况说明字符串B不是字符串A的子串
代码:
# include
# include
# include
int BF(char *s, char *p, int pos)
{
assert(s != NULL && p != NULL);
int lens = strlen(s);
int lenp = strlen(p);
if (lenp > lens)
{
return -1;
}
int i = pos;
int j = 0;
while (i < lens && j < lenp)
{
if (s[i] == p[j])
{
i++;
j++;
}
else
{
i = i - j + 1;
j = 0;
}
}
if (j >= lenp)
{
return i - j;
}
return -1;
}
int main()
{
char *s = "abcababcabc";
char *p = "abcabc";
printf("%d\n", BF(s, p, 0));
return 0;
}
1、KMP算法思路
i
A:a b c a b a b c a b c
j
B: a b c a b c
如上图所示,当在下标为i处失配。则存在:
B[0] ... B[k - 1] == A[i - k] ... A[i - 1]
A[i - k] ... A[i - 1] == S[i - k] ... S[i - 1]
可推出:B[0] ... B[K - 1] == B[j - k] ... B[j - 1]
因此:j不必退回0,i也不必退回 处,只需j退回到字符k处,如下:
i
A:a b c a b a b c a b c
j
B: a b c a b c
然后:接着i 和 j处接着往下匹配即可,失配之后继续按照上述方法回退
2、next数组
引入next数组就是为了求出上述k的数值,next数组长度和字符串A长度相同。
next[i]存储的数值是:从A[0] - A[i]组成的字符串中两个相等真子串的长度,这两个满足以下条件
例:字符串A: "abcababcabc"的next数组如下
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
next | -1 | 0 | 0 | 0 | 1 | 2 | 1 | 2 | 3 | 4 | 5 |
真子串 | "a" | "ab" | "a" | "ab" | "abc" | "abca" | "abcab" |
void GetNext(const char *p, int *next) //求next数组
{
next[0] = -1;
next[1] = 0;
int j = 1;
int k = 0;
int lenp = strlen(p);
while (j + 1 < lenp)
{
/*
*当k == -1 时,该位置对应的next应为0
*如果p[k] == p[j],则p[j+1] == k+1
*/
if (k == -1 || p[k] == p[j])
{
next[++j] = ++k;
}
/*
*当p[k] != p[j],则相当于子串匹配问题,用KMP算法思路,k回退,继续循环匹配
*/
else
{
k = next[k];
}
}
}
3、kmp算法流程
依然用i 和 j 分别表示字符串AB的下标,初始值都为0。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
(1)跟BF匹配方式相同,第一次当i = j = 5时失配
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j | |||||||||||
next | -1 | 0 | 0 | 0 | 1 | 2 | 1 | 2 | 3 | 4 | 5 |
此时,j没有必要一定退到0,只需回退到next[j]即可,而i可以不用回退,如下:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j | |||||||||||
next | -1 | 0 | 0 | 0 | 1 | 2 | 1 | 2 | 3 | 4 | 5 |
(2)然后判断B[j]是否和A[i]相等,如果相等i++,j++,很明显,此时不相等,那么j继续回退到next[j],如下:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | |||||||||||
字符串B | a | b | c | a | b | c | |||||
j | |||||||||||
next | -1 | 0 | 0 | 0 | 1 | 2 | 1 | 2 | 3 | 4 | 5 |
(3)此时B[j] == A[i],则i++,j++
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
字符串A | a | b | c | a | b | a | b | c | a | b | c |
i | j |
||||||||||
字符串B | a | b | c | a | b | c | |||||
j |
|||||||||||
next | -1 | 0 | 0 | 0 | 1 | 2 | 1 | 2 | 3 | 4 | 5 |
(4)重复上述步骤,直到 j 走到字符串B末尾,则说明从i - j处开始B串是A串的子串。如果i走到A串末尾 j 都没有走到B串末尾,则说明串B不是A串子串。
4、代码
int KMP(const char *s, const char *p, int pos)
{
assert(s != NULL && p != NULL);
int lens = strlen(s);
int lenp = strlen(p);
if (lens < lenp)
{
return -1;
}
int i = pos;
int j = 0;
int *next = (int *)malloc(sizeof(int) * lenp);
assert(next != NULL);
GetNext(p, next); //O(m)
while (i < lens && j < lenp)
{
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
free(next);
if (j >= lenp)
{
return i - j;
}
return -1;
}