数据结构——kmp匹配

朴素做法先略过~

假设在主串 i 之前都匹配一致,在主串 i  + 1时s[i + 1]与p[ j ]突然不再匹配,如果是暴力做法的话需要把整个p串后移一位,但是由于我们已经匹配过一段长度了(ab段)这里面有很多额外的信息,通过这个我们可以进行优化

现在我们来找在某一次匹配失败之后模板串最多往后移动多少它可以继续匹配

可以看出铅笔所圈住的三处子串他们是相同的,所以要找的这个最多要移动多少只与它模板串自身有关,这个东西如果可以预处理出来就很棒,就可以省略很多不必要的匹配从而提高时间效率

再来看一下,其实模板串中每个点都对应了一个 最多要移动多少 的值。我们把这样的值和其下标用next数组存放。

next[ i ] = j   含义:j 的值就是 模板串中以1为起点的前缀和以 i 为终点的后缀,这个前缀和后缀相等的最大长度。即p[1,i] = p[i - j + 1, i]

 

接下来看一下匹配的实际过程是怎么样的:

 

假设在s[ i ]之前都是匹配的,在s[ i ]时突然不匹配了,这样就直接让模板串p移动到下一个next[ j ]即可.如果s[ i ]与p[ j + 1] 匹配则继续进行匹配,否则继续让j前移(j = next[ j ])

由于每次都是看的是s[ i ]与p[j + 1]是否匹配,所以i是从1开始匹配,j从0开始匹配

for(int i = 1, j = 0; i <= m ; i ++)
{
    while(j && s[ i ] != p[j + 1]) j = next[ j ];
    if(s[ i ] == p[ j + 1 ]) j ++; 
    if( j == n )//n是p串(模板串)的长度
    {
        //匹配成功后的操作
    }
}

while循环离开条件有两个,第一个就是 j 为0,j退无可退了,这个时候换下一个i重新进行。第二个就是 s[i]与o[j+1]匹配了就继续向下看是否匹配。

 

然后再来看一下求next数组的过程 :

 求next数组的过程其实是和匹配过程是类似的(我kmp是听y总讲的),下面来看看

比如要求模板串第i个点对应的next数组的值(next[i]),其实求的还是在第i个不匹配时,p串后移最少多少能继续匹配。(模板串和模板串自身匹配)

当第i个不匹配的时候就让j退到下一个next[j](直到找到相匹配的为止,有点递归的意思),然后去判断第i个是否能匹配,如果匹配就j++,然后用next[i] = j记录下来对这个i找到的这个next[i],注意i是从2开始的(只有i大于2的时候才对称可能存在所要的匹配),next[1] = 0,第一个不匹配的时候,这个所要找的前缀和后缀相同的最大长度为0.

for(int i = 2,j = 0;i <= n;i ++)
{
    while(j && p[i] != p[j + 1] ) j = next[j];
    if(p[i] == p[j + 1]) j ++;
    next[i] = j;    
}

注意求next数组的时候是p串和p串自身去匹配。首先i从2开始,因为只有i大于等于2的时候才有可能是对称的,这样才有可能存在前缀和后缀一说。

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

 

最后,kmp的模板:

//求next数组:
for(int i = 2,j = 0; i <= n ; i ++)
{
    while(j && p[i] != p[j + 1]) j = next[j];
    if(p[i] == p[j + 1]) j ++;
    next[i] = j;
}
//匹配过程
for(int i = 1,j = 0;i <= m;i ++)
{
    while(j && s[i] != p[j + 1]) j = next[j];
    if(s[i] == p[j + 1])j ++;
    if(j == n)
    {
        j = next[j];//每次匹配成功之后,如果要继续匹配就让j后退一次,接着往下一个i去匹配
        //成功逻辑
    }
}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rds.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值