字符串匹配——KMP算法

先附上一份我觉得解释很详细的文章:
匹配基本流程和前后缀
next数组求法与解释
KMP算法之所以是线性复杂度,就在于不需要重复匹配对比,通过next调到最近的有必要匹配检查的位置。
换句话说,就是对于被匹配串,每当在当前节点发生不匹配,为了不浪费之前匹配成果和避免遗漏,跳到拥有相同前缀的位置继续比较。
不懂?不要紧,看着下面代码注释,相信会有新的发现。
奉上一道水题:
剪花布条
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
Sample Input
abcde a3
aaaaaa aa
#
Sample Output
0
3

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxsize 1010
//str1是源字符串,str2是需要寻找的字符串
char str1[maxsize],str2[maxsize];

//记录跳表
int Next[maxsize];

//获得跳表
void getnext()
{
    int i=0,j=-1;//i是当前找到的位置,j是从开头找
    //
    Next[0]=-1;
    int m=strlen(str2);
    //为每一个位置上的字母寻找跳表值
    while(i<m)
    {
        //如果是-1,代表刚开始或者前一位置匹配失败,且不存在拥有相同前缀的组成
        //后者比较好理解,就是前缀和后缀当前位置拥有相同字符,
        //每个位置next的值是当前位置前缀和后缀拥有的最大公共长度
        //当新读入的一位和前缀读到的位置的字母相同,自然最大长度会加一
        if(j==-1||str2[i]==str2[j])
        {
            i++;
            j++;
            Next[i]=j;
        }
        //新读入的一位和前缀读到的位置的字母不相同,自然最大长度从头开始重新找
        //因为前缀一定从第一位开始向后计算
        //所以一旦不一样,就可以跳到第next【j】个位置
        //(如果j不为0,说明有一段是匹配的,也就是从开头到第j个之前还是一样的)
        //此时,如果是-1,说明之前从没有到达这个长度,此时只能从头开始重新计算,接上一步j==-1
        //如果不是-1而是确切的值,说明之前有过有相同前缀的子串,则在此基础上继续找,接上一步str2[i]==str2[j]
        else
        {
            j=Next[j];
        }
    }

}
//匹配过程
int kmp()
{
    int n=strlen(str1);
    int m=strlen(str2);

    int i=0,j=0,cnt=0;
    while(i<n)
    {
        //如果重头来或者相同
        if(j==-1||str1[i]==str2[j])
        {
            i++;
            j++;
        }
        else
        {
            j=Next[j];
        }
        //完全匹配
        if(j==m)
        {
            j=0;
            str1[i-1]='0';
            cnt++;
        }
    }
    return cnt;
}
int main()
{
    while(scanf("%s",str1))
    {
        memset(Next,-1,sizeof(Next));
        if(str1[0]=='#') break;
        else
        {
            scanf("%s",str2);
            getnext();
            printf("%d\n",kmp());

        }
    }
    return 0;
}

本人萌新小白一枚,若有错误,还望不吝赐教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值