模式匹配算法

串的模式匹配设有两个字符串S和T,设S 为主串,设T为子串。在主串S中查找与模式T相匹配的子串,如果匹配成功,确定相匹配的子串中的第一个字符在主串S中出现的位置。
一、 BF算法:
1. 指针i和j 分别指示主串S 和模式串T 中当前待比较的字符位置,i 和j初始值为1;
2. 取k来代替i从当前位置开始与j相匹配,此时的i用来记录它本来的值 而k是要随着j的变化而变化的
3. 遇到不匹配的情况时,从i的下一位开始与j进行再一次的比较
这种方法简单易理解易实现 但是过于冗余 复杂度较高

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main()//BF算法,从主串的第一位开始 主串与模式串比较,不匹配时主串的指针要回去从下一位开始匹配
{
    int i,j,k,ans=-1;
    char S[1001],T[1001];
    scanf("%s%s",&S,&T);
    int lens,lent;
    lens=strlen(S);//主串的长度
    lent=strlen(T);//子串的长度
    for(int i=0;i<lens;++i){//从下一位重新开始匹配
        for(j=0,k=i;j<lent&&k<lens;++j){
            if(S[k]==T[j])
                ++k;
            else//也即 此时不匹配跳出匹配过程
                break;
        }
        if(j==lent){
            ans=i;
            break;
        }
    }
    printf("%d\n",ans);//输出开始位置 -1为未匹配成功
    return 0;//此方法虽然能够找到答案,但是时间复杂度很高 重复的过程很多即很慢
}

二、KMP
上面的BF算法 不如说 在S串中找到位置k 从k开始与T匹配 之前 需要遍历S从0到k 而kmp算法所做的工作就是让我们不必从0到k一步一步都遍历一遍 而是跳着匹配 这就要求我们在遇到不匹配的情况时需要知道下一步跳到哪里开始重新匹配 也就是kmp中的next数组了。例如S 串为:ababcabcacbab T串为:abaca
这里写图片描述

而所谓的 合适的 位置是如何得来的呢
现在假设应与T串中的第k个字符继续比较 例如上图第二次匹配,则T串的从1到k-1个字符应该是与S串的从(此时的i是此次匹配开始值i=8)i-k+1到i-1个字符相匹配的,而在第一次匹配过程中可以发现,S串的从(i=8, j=8)i-k+1到i-1个字符是与T串中j-k+1到j-1个字符是匹配的,所以第二次开始时跳到k=4是因为
T串的从1到k-1个字符 和T串的从j-k+1到j-1个字符相匹配(j=8)
也就是说kmp基于的是模式串即T串本身的一种 轮回?现在就是在基于模式串来求next数组,以发生不匹配时可以跳到合适的位置
当j=1时next[j]=0
next[j]=Max{ k| 1< k < j && T1…..Tk=Tj-k+1……Tj-1}即寻找最大的轮回
当k=1时 也就是没有所谓的轮回时next[j]=1
在匹配过程中 若Si==Tj 或者j==0则 i j 都自增1 否则 j 跳到next[j]位置
然后就是求next数组了 由next数组的定义可知next[1]=0
假设next[j]=k 这表示在T串中 T1…..Tk-1 = Tj-k+1……Tj-1
讨论next[j+1]的值 当Tk==Tj 时 显然 next[j+1]=k+1;不等时 仔细想一下 这个过程是不是刚好是串的模式匹配过程 不过这里主串和模式串都是T串 不等时把身为模式串的T串右移 右移到哪呢 到next[k]啊(这里可以拿纸笔自己演算) 默认的存在 k < j 所以此时next[k]是已知条件 这时若Tj==Tnext[k] 则 next[j+1]=next[k]+1 不等就继续找啊 类似dfs一层一层往下找 直到Tj!=T1 此时next[j+1]=1(最好的理解方式就是拿纸笔从头到尾自己演算一遍 不省略任何一个步骤)
ok 这里说明一下 由于本人偷懒 简陋的代码里字符串是从0开始存的 next[0]=-1 中心思想是没变的

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
    int next[1001];
    char S[1001],T[1001];
    scanf("%s%s",&S,&T);
    int lens=strlen(S);
    int lent=strlen(T);
    int i=0,j=-1,ans=-1;
    next[i]=j;
    while(i<lent){
        if(j==-1||T[i]==T[j]){
            ++i;
            ++j;
            //if(T[i]!=T[j])
                next[i]=j;
            //else
                //next[i]=next[j];
        }
        else
            j=next[j];
    }
    i=0,j=0;
    while(i<lens){
        if(j==-1||S[i]==T[j]){
            ++i;
            ++j;
        }
        else
            j=next[j];
        if(j==lent){
            ans=i-j;
            break;
        }
    }
    printf("%d\n",ans);//此答案为从0开始计数。。。
    return 0;
}

最后讲一下求nextval数组 可以发现 代码中注释掉的部分 添上就是求nextval数组了
这里发现一件很有意思的事情 之前求next数组时 next[j]=k 求next[j+1]值时第一种情况就是Tj==Tk 现在讨论这种情况下 主串S和模式串T匹配过程中刚好到 j 这个位置失配了 此时 由next[j]=k 就要跳到 k 这个位置进行比较了 可是Tj==Tk啊 不用比较就知道不匹配啊 可是前面求的时候就这样求的 错了吗 没啊 前面没错 这里也没错 可是怎么就不一样了呢 因为前面是根据这两个求的是next[j+1]的值啊 讨论的是j+1不匹配的时候跳到哪里 这里是j不匹配跳到哪里 ok 清楚就行
nextval数组处理的就是上面这种情况 减去无谓的匹配 这也就有了代码中的注释部分
最后 此篇文章借鉴于严蔚敏等主编的数据结构(C语言版)课本 有错欢迎指出 谢谢
    

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值