【数据结构】KMP算法

滑动模式串

KMP算法对于字符串匹配的改进在于当出现“失配”(即主串第i个字符和模式串第j个字符不同)的情况,并不需要回溯i指针,而是通过滑动模式串使得模式串之前从头开始的子串得到部分匹配。例如

a b c a b c a b c

a b c a b d

第6个字符失配,所以滑动模式串如下

a b c a b c a b c

a b c a b d

其中第4个字符和第5个字符得到部分匹配,接着继续进行第6个字符的匹配。

描述过程

令主串为模式串为,当si和pj失配时,i指针不进行回溯,对j指针进行回溯(即滑动模式串)找到模式串第k个字符再与si进行匹配,而模式串从1到k-1的子串要与主串从i-k+1到i-1的子串要匹配,即,既然满足这个要求,相当于模式串从1到k-1的子串要和模式串j-k+1到j-1的子串要匹配,即

next数组

我们用next数组来记录滑动模式串的长度,即当si和pj失配时,我们要找到j之前的一个字符再与si进行匹配,next数组记录的就是找到的那个字符在模式串数组的下标,这里我们没有利用第一个元素(next[0])方便分析。

j

1 2 3 4 5 6 7 8

模式串

a b a a b c a c

next[j]

0 1 1 2 2 3 1 2

利用next数组进行匹配

我们在已经构建了next数组的情况下,若出现si和pj失配的情况,则将j指针滑动到next[j]的位置,就总会有第next[next[next...[j]]]与si匹配,然后i和j同时加一进行下一个字符的匹配,否则最后j滑动为0,i和j加一重新进行匹配。

int Index_KMP(char* s, char* p,int* next)
{
    int i = 0, j = 0;
    while(i <= slength && j <= plength)\\设有主串s,模式串p的长度slength和plength
    {
        if(j == 0 || s[i] == p[j])i++, j++;
        else j = next[j];
    }
    if(j > plength)return i - plength;\\匹配成功就返回主串成功匹配的第一个位置
    else return 0;
}

构建next数组

构建next数组的过程就相当于把模式串也当作主串,和自己进行匹配,要得到next[i]=k,使得

,k要尽可能大。有两种情况。

第一种情况为pi=pj,则next[j+1]=k+1,因为存在

第二种情况为pi和pj失配,则需要滑动模式串找到第一个和pi匹配的k,即,然后next[i+1]=k+1,假如k=0,则没有得到部分匹配,则只能重新匹配,第i个字符要和第一个字符开始匹配,即next[i]=1。

void get_next(char* p, int* next)
{
    int i = 1, j = 0;
    next[1] = 0;
    while(i < slength)
    {
        if(j == 0 || p[i] == p[j])p[++i] = p[++j];
        else j = next[j];
    }
}

优化

当模式串中有连续的字符出现时,例如模式串“aaaab”和主串“aaabaaaab”进行匹配,在第四个字符失配时,按照next数组模式串向右滑动,但是滑动后字符是一样的,都会失配,所以我们需要将模式串一口气滑动到底就能节约时间,所以对next数组进行改进,假如第next[i]=k个字符和第i个字符是一样的,只需将next[i]=next[k],即

p

a a a a b

i

1 2 3 4 5

next[i]

0 1 2 3 4

nextval[i]

0 0 0 0 4

void get_nextval(char* p, int* nextval)
{
    int i = 1, j = 0;
    next[1] = 0;
    while(i < slength)
    {
        if(j == 0 || p[i] == p[j])
        {
            i++,j++;
            if(p[i] != p[j])nextval[i] = j;
            else nextval[i] = nextval[j];
        }
        else j = next[j];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值