您的位置:首页 > 其它

改进的KMP算法

2016-05-27 21:48 513 查看
KMP算法是字符串间进行匹配的一种算法,区别于一般的简单模式匹配算法。

假设有主串s和子串t,判断t是否是s的子串???

一般的算法是将s[i]和t[j]逐个比较,当s[i]==t[j]时i++;j++;当s[i]!=t[j]时,i和j的值回溯i=i-j+1;j=0;这种算法当遇到子串t有较多元素重复或者s和t有较多相同时是比较低效的,

所以采用KMP算法。

KMP算法的核心是采用一个数组next存储子串t每次回溯的位置,且i的值不用回溯。

下面我们看一个例子:

主串:a b c a b c a b c.....

下标:1 2 3 4 5 6 7 8 9

子串:a b c a b x

下标:1 2 3 4 5 6

很明显主串和子串前5个字符是相等的,但第六个c和x不相等,采用KMP的话就可以i的值为6,然后j的值为3,也就是这样

                         i

主串:a b c a b c a b c.....

下标:1 2 3 4 5 6 7 8 9

                         j

子串:        a b c a b x

下标:        1 2 3 4 5 6

至于j为什么是3,其实是由子串s自己决定的。

子串abcabx的next数组:

next: 0  1  1  1  2  3

首先next[1]=0,然后next[2]看前面有多少个前后缀是相同的,很明显next[2]是0个所以是1,next[3]是ab也是0个所以是1,next[4]是abc也是1,next[5]是abca,a和最后一个a相同,所以next[5]=2,next[6]是abcab有前面两个ab和后面两个ab相同,所以next[6]=3.

求next数组的函数:

void get_next(char *t,int *next,int t_lenth)

{

       int i,j;

       i=1;j=0;

       next[1]=0;

       while(i<t_lenth)

      {

          //t[i]表示后缀字符,t[j]表示前缀字符

          if(j==0||t[i]==t[j])

          {

                i++;

                j++;

                next[i]=j;

           }

          else 

                j=next[j];//回溯

       }

}

主函数:

int main(void)

{

char S_str[MAXSIZE],T_str[MAXSIZE];

int nextval[MAXSIZE];

int s_lenth,t_lenth;

int N,i,j,k;

scanf("%d",&N);

for(k=0;k<N;k++)

{

scanf("%s",S_str+1);

scanf("%s",T_str+1);


s_lenth=0;j=1;//主串长度

while(S_str[j++]!='\0') s_lenth++;


t_lenth=0;j=1;//子串长度

while(T_str[j++]!='\0') t_lenth++;


//计算nextval数组

get_nextval(T_str,nextval,t_lenth);


//查看是否匹配

i=j=1;

while(i<=s_lenth&&j<=t_lenth)

{

if(j==0||S_str[i]==T_str[j])

{

i++;

j++;

}


else

j=nextval[j];

}


if(j>t_lenth)

printf("yes\n");

else printf("no\n");

}


return 0;

}


改进的KMP算法:

其实是为了预防子串是:xxxxxxxb这种前面有多个重复的情况

假设主串:aaaaaaccc....

       子串:aaaaaab

运用原来的KMP,i的值会到7,根据next数组,j的值会从7到1,前面6个a是都是相同的,第6个a和c不同,为什么还要比较下去呢?所以我们对next数组进行了优化成nextval数组。

求nextval数组的函数:

//计算nextval数组函数

void get_nextval(char *t,int *nextval,int t_lenth)

{

int i,j;

i=1;j=0;

nextval[1]=0;

while(i<t_lenth)

{

if(j==0||t[i]==t[j])//t[i]表示后缀字符,t[j]表示前缀字符

{

i++;

j++;

if(t[i]!=t[j])//当前字符与前缀字符不同

nextval[i]=j;

else

nextval[i]=nextval[j];

}

else

j=nextval[j];//j回溯

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: