字符串的学习(2)-KMP匹配算法

先看下图:


从图中,我们可以很容易的发现,S不必回溯到S[1]的位置,T也不必回溯到T[0]的位置,因为前面的字符,S和T中都是相等的。如果S不回溯的话,那T该怎么办呢?我们也可以很容易的发现,S中5、6、7号字符和T中0、1、2号字符是相等的。故T直接回溯到T[3]的位置即可。此时我们就省去了很多不必要的回溯和比较。那么这些都是我们从图中直观得出的结论,如果换做其他字符,我们又如何知道T该回溯到几号字符呢?

先看看KMP算法的思想:假设在模式匹配的进程中,执行T[i]和W[j]的匹配检查。若T[i]=W[j],则继续检查T[i+1]和W[j+1]是否匹配。若T[i]<>W[j],则分成两种情况:若j=1,则模式串右移一位,检查T[i+1]和W[1]是否匹配;若1<j<=m,则模式串右移j-next(j)位,检查T[i]和W[next(j)]是否匹配。重复此过程直到j=m或i=n结束。(摘抄自百度百科)      不知其他人怎样,反正我是一看这些就晕。说的简单点,KMP算法的重点就是研究当某个字符不匹配时,T该回溯到什么位置去,以避免多余的回溯和比较过程。如果要确定字符不匹配时,T应该回溯到什么位置,那么我们就要对T字符串进行预处理。这里,我们用一个int型的 next 数组来标识T中当各个元素失配时,T 下标应该回溯到的位置。下面,还是用一张图片讲解:


图中,首先构造 Next 数组,构造过程见图解(这里讲解的简单了些,本文重点是理清KMP算法思路,故没有赘述,想细究的同学,自己谷歌一下吧)。构造完毕后,当S[8]和T[8]失配时,我们从next 数组可知,T应回溯到T[3]的位置,重新开始比较。样子有点像下面这样:


如果S[8]和T[3]再次失配,则继续回溯,即比较S[8]和T[0]。如果再次失配,T已无回溯元素可言,此时,S向后移动,即开始比较S[9]和T[0]……结束条件就是:到达字符串S或者T的结尾。若是S结尾,则返回-1.若是T结尾,则匹配成功。返回S中T串开始时的下标即可。

下面给出个小例子,仅供大家练习使用:


获得next 数组的算法

void get_next(char *T,int *next)
{
    int i = 1;
    int j = 0;
    next[1] = 0;//第一个不匹配,则将子串数组移到下标为0这个位置

    while(i < T[0])//判断头尾相同数  有一个相同则next[i]=2,两个则为next[i]=3
    {
        if(0 == j || T[i] == T[j])
        {
             i++;
             j++;
             next[i] = j;
        }
        else
        {
             j = next[j];
        }
    }
}
KMP匹配算法,与BF算法相似

KMP是子串动,主串不动

BF是主串动,子串不动,遇到不相等就回溯到第一个数

int index_KMP(char *s,char *t)
{
    int i=1,j=1;
    int next[255];
    get_next(t,next);
    while(i<=s[0]&&j<=t[0])
    {
        if(j==0||s[i]==t[j])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
    }
    if(j>t[0])
    {
        return i-t[0];
    }
    else
        return 0;
}

完整代码:
#include <stdio.h>
#include<stdlib.h>
void get_next(char *T,int *next)
{
    int i = 1;
    int j = 0;
    next[1] = 0;//第一个不匹配,则将子串数组移到下标为0这个位置

    while(i < T[0])//判断头尾相同数  有一个相同则next[i]=2,两个则为next[i]=3
    {
        if(0 == j || T[i] == T[j])
        {
             i++;
             j++;
             next[i] = j;
        }
        else
        {
             j = next[j];
        }
    }
}
int index_KMP(char *s,char *t)
{
    int i=1,j=1;
    int next[255];
    get_next(t,next);
    while(i<=s[0]&&j<=t[0])
    {
        if(j==0||s[i]==t[j])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
    }
    if(j>t[0])
    {
        return i-t[0];
    }
    else
        return 0;
}

int main()
{
    char str1[255] = "xababaaabaaa";
    char str2[255]="xaab";
    str1[0]=11;
    str2[0]=3;
    int num;
    num=index_KMP(str1,str2);
    printf("%d\n",num);
    return 0;
}


KMP算法的优化;

主要是改善next数组

void get_next(char *T,int *next)
{
    int i = 1;
    int j = 0;
    next[1] = 0;//第一个不匹配,则将子串数组移到下标为0这个位置

    while(i < T[0])//判断头尾相同数  有一个相同则next[i]=2,两个则为next[i]=3
    {
        if(0 == j || T[i] == T[j])
        {
             i++;
             j++;
             if(T[i]!=T[j])  //主要改变的地方 想想:如果是aa...aa则回溯的位置是一个a的next就可以
             {
             next[i] = j;
             }
             else
                next[i]=next[j];
        }
        else
        {
             j = next[j];
        }
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值