hihocode-1015-KMP算法介绍

本文介绍了hihocode1015题目的KMP算法,包括算法原理、NEXT数组的概念和求解,以及如何利用NEXT数组进行字符串匹配,通过代码样例展示了KMP算法在解决模式串查找问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hihocode1015-KMP 算法探讨

题目如下

输入
第一行一个整数N,表示测试数据组数。接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。其中N<=20

输出
对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。

样例输入

5
HA
HAHAHA
WQN
WQN
ADA
ADADADA
BABABB
BABABABABABABABABB
DAD
ADDAADAADDAAADAAD

样例输出

3
1
3
1
0

算法解释

KMP引出
源 串:babababababababb
模式串:bababb

请问原串中有几个这样的模式串?
一目了然,只有一个,但如果用计算机程序去判别的话,用一个一个去匹配的方法十分浪费时间。

源 串:babab|abababababb
模式串:babab|b

发现在第6 个字符处有了分歧和误差,此时有两种情况
第一,源串和模式串的新的对齐点在此字符之后(包括此字符);
第二,源串的这字符正好和模式串的某个字符匹配对齐;
在这里我们讨论第二种情况

源串:            babab|abababababb
模式串(对齐点=1):babab|b
模式串(对齐点=3):  bab|abb

我们会发现这样一个问题,假设我们以i表示两个串发生分歧的第一个地方,用j来表示到达新的对齐点模式串移动的字符个数,我们会发现这样一个规律,对于模式串而言有[1,j]和[j,2j-1]是相等的,我们回发现在[1,2j-1]段,模式串的字符是对称的,而i为第一个发生分歧的点,则有2j-1=i-1,因此这个等式可以换成这样[1,i-j]=[j,i-1],这有这样的情况下我们才能进入到分歧点i的匹配环节,因此我们需要对模式串的每一个分歧位置位置寻找这样的一个j值。这些j值就可以组成一个新的数组,这个数组就是NEXT数组。这个定义还不是很好,还需要更加精确的定义。

NEXT数组

NEXT[0]=-1
NEXT[i]=max{0<=k

NEXT数组的应用

知道了NEXT数组,我们需要知道如何使用NEXT数组
针对我们所距离的模式串有

模式串:bababb
NEXT001231

于是针对源串ori[p]和模式串par[q],我们有

源串(p=5):  babab|abababababb
模式串(q=5):babab|b

此时匹配到第五个字符时就无法向下匹配,因为ori[p+1]!=par[q+1],可以令q=NEXT[q],则有

源串(p=5): babab|ab|ababababb
模式串(q=3):  bab|ab|b

此时发现ori[p+1]==par[q+1],则可以往下匹配,于是p++,q++,当p=7,q=5时,又出现了不匹配。

源串(p=7): bababab|ababababb
模式串(q=5):  babab|b

此时可另q=NEXT[q],依次类推下去,当出现q=-1是则表明模式串越过了匹配点,此时需要跳过这个点,p++,q++,然后接着经行判断

NEXT数组求解

知道的NEXT数组的用法,我们需要知道NEXT如何求来
如果我们已知NEXT[1..4],如何知道NEXT[5]?
我们可以令par.substring(1..5)为源串,par.substring(1..4)为模式串,即

源串(p=4): baba|b  
模式串(q=4):baba

模式串第五个字符是空字符,故肯定无法匹配,可以令q=NEXT[q],则

源串(p=4): baba|b
模式串(q=2): ba|ba

p++,q++时,我们发现ori[p]==par[q],可以匹配上,而此时NEXT[p]=q
即NEXT[5]=3。

代码样例

#include <stdio.h>
#include <string.h>

int main() {
    int n;
    char p[10010],s[1000010];//p为模式串,s为源串
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s%s",p,s);
        int next[10010]={};
        int i=0,j=-1;
        next[0]=-1;
        int lenp=strlen(p);
        int lens=strlen(s);
        while(i<lenp)         //next数组的生成
        {
            if(j==-1||p[i]==p[j])
                next[++i]=++j;
            else
                j=next[j];
        }
        int ans=0;
        i=j=0;
        while(i<lens) 
        {
            if(j==-1||s[i]==p[j])
            {
                ++i;++j;
            }
            else
                j=next[j];
            if(j==lenp) ans++;
        }
        printf("%d\n",ans );
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值