next数组的简单求法c语言,图解 朴素匹配,KMP算法 next数组 nextval数组求解 及代码实现...

本文对比了朴素匹配的暴力查找策略,其时间复杂度较高,然后深入解析了KMP算法,通过预计算部分匹配值数组优化匹配过程,将时间复杂度降低到O(m+n)。通过实例和代码展示了如何使用KMP算法在字符串中高效查找子串。
摘要由CSDN通过智能技术生成

朴素匹配

朴素匹配又称暴力匹配,其时间复杂度为O(m*n)

缺点:时间复杂度高

例如:在ABEECDEEF中查找是否有子串EEF

1.

ABEECDEEF

EEF

将A于E匹配发现不相同,子串回到起点,主串后移一位

2.

ABEECDEEF

EEF

B与E匹配发现不相同,子串回到起点,主串后移一位

3.

ABEECDEEF

EEF

E与E匹配成功,子串和主串都后移一位,继续匹配

4.

ABEECDEEF

EEF

此时,E与E匹配相同,子串和主串都后移一位,继续匹配

5.

ABEECDEEF

EEF

此时,F与C匹配不相同 ,子串回到起点,主串回到第一个匹配上的字符的下一个位置(回到第二个E)

6.

ABEECDEEF

EEF

继续匹配,直至遇到第一个匹配上的字符

......

7.

ABEECDEEF

EEF

E与E匹配相同,子串和主串都后移一位,继续匹配

8.

ABEECDEEF

EEF

E与E匹配相同,子串和主串都后移一位,继续匹配

9.

ABEECDEEF

EEF

F与F匹配相同,此时子串已匹配完,匹配完成

代码如下:

#include#include#includeint BF(const char *str,const char *sub,int pos)

{

assert(str != NULL && sub != NULL);

int i = pos;

int j = 0;

int lens = strlen(str);

int lensub = strlen(sub);

while(j < lensub && i < lens)

{

if(str[i] == sub[j])

{

i++;

j++;

}

else

{

i = i-j+1;

j = 0;

}

}

if(j >= lensub)

{

return i-j;

}

else

{

return -1;

}

}

KMP算法

KMP算法是对朴素匹配的一个优化,在于匹配不成功时,子串有时可以不用回到起点,主串不用回到之前的某个位置

其时间复杂度为O(m+n),过程如下:

1.

BBC ABCDAB ABCDABCDABDE

ABCDABD

B与A不匹配,所以主串后移一位。

2.

BBC ABCDAB ABCDABCDABDE

ABCDABD

因为B与A不匹配,主串再往后移。

3.

BBC ABCDAB ABCDABCDABDE

ABCDABD

就这样,直到主串有一个字符,与子串的第一个字符相同为止。

4.

BBC ABCDAB ABCDABCDABDE

ABCDABD

接着比较子串和主串的下一个字符,还是相同。

5.

BBC ABCDABABCDABCDABDE

ABCDABD

直到主串有一个字符,与子串对应的字符不相同。(此时,空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。)

空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

移动位数 = 已匹配的字符数 - 对应的部分匹配值

所以,将子串回退4位,继续匹配

6.

BBC ABCDAB ABCDABCDABDE

ABCDABD

空格与C不匹配,这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。

所以,移动位数 = 2 - 0,结果为 2,于是将子串回退2位

7.

BBC ABCDAB ABCDABCDABD

ABCDABD

空格与A不匹配,主串后移一位

8.

BBC ABCDAB ABCDABCDABDE

ABCDABD

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,将子串回退4位。

9.

BBC ABCDAB ABCDABCDABDE

ABCDABD

逐位匹配

10.

BBC ABCDAB ABCDABCDABDE

ABCDABD

直到搜索词的最后一位,发现完全匹配,于是匹配完成。

代码如下:

int Kmp(const char *str,const char *sub,int pos)

{

int i = pos;

int j = 0;

int lens = strlen(str);

int lensub = strlen(sub);

int *next = (int *)malloc(sizeof(int) * lensub);

assert(next != NULL);

GetNext(next,sub);

while(j < lensub && i < lens)

{

if(j == -1 || str[i] == sub[j])

{

i++;

j++;

}

else

{

j = next[j];

}

}

if(j >= lensub)

{

return i-j;

}

else

{

return -1;

}

}

next数组(部分匹配值)

next数组就是一个模式串的前缀和后缀的最大公共串长度

"前缀"指除了最后一个字符以外,一个字符串的全部头部组合;

"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

拿上述KMP算法的例子来说,求子串“ABCDABD”的next数组过程如下:

1.

A B C D A B D

-1 0

默认next[0]=-1,next[1]=0

2.

A B C D A B D

-1 0 0

"AB"的前缀为[A],后缀为[B],共有元素的长度为0;

3.

A B C D A B D

-1 0 0 0

"ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

4.

A B C D A B D

-1 0 0 0 0

"ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

5.

A B C D A B D

-1 0 0 0 0 1

"ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

6.

A B C D A B D

-1 0 0 0 0 1 2

"ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

代码如下:

void GetNext(int *next,const char *sub)

{

next[0] = -1;

next[1] = 0;

int lensub = strlen(sub);

int i = 2;//当前的i

int k = 0;//前一项的K值

while(i < lensub)

{

if(k == -1 || sub[i-1] == sub[k])

{

next[i] = k+1;

i++;

k = k+1;

}

else

{

k = next[k];

}

}

}

nextval

下标

0

1

2

3

4

5

6

模式串(P)

A

B

C

D

A

B

D

next

-1

0

0

0

0

1

2

nextval

-1

0

0

0

-1

0

2

nextval数组的值怎么来的呢??????????

默认nextval[0]=-1

从1号下标开始比较,B和next[0]下标对应的A是否相等    (竖着看 竖着看!!同一列 )

相等时  nextval[1]=A的nextval值

不相等时 nextval[1]=B的next值

从2号下标开始比较,C和next[0]下标对应的A是否相等

相等时  nextval[1]=A的nextval值

不相等时 nextval[1]=C的next值

以此类推.....

代码如下:

void GetNextval(int *next,const char *sub)

{

nextval[0] = -1;

int lensub = strlen(sub);

int i = 1;//当前的i

int k = -1;//前一项的K值

while(i < lensub)

{

if(k == -1 || sub[i-1] == sub[k])

{

i++;

k = k+1;

if (nextval[i] != p[j])

{

nextval[i] = k;

}

else

{

nextval[i] = nextval[k];

}

}

else

{

k = nextval[k];

}

}

}

KMP算法 C语言  next数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值