KMP模式匹配算法

KMP是字符串匹配中的一个算法,目的是快速在一个较长字符串中匹配出和给定字符串相等的子串。

朴素算法

常规的匹配算法又称为朴素字符串匹配,主要是通过逐个位置比较,不相等时进行回溯,主串从开始匹配的位置前进一个字符位置然后和要匹配的字符串第一个位置继续进行比较,这样会进行大量的重复比较。
例如:
主串:S=”abcdefgh…”
模式串:T=”abcdefx”
朴素的匹配算法匹配时,先从S串和T串第一个位置依次匹配,当匹配到S串的’g’和T串的’x’不相等时,然后进行回溯,从S串的第二个字符’b’和T串的第一个字符’a’开始比较,不断重复上述操作,直到在S中找到和T匹配的子串。

KMP匹配算法原理

KMP算法主要思想:朴素算法需要不断回溯,进行了大量没必要的比较,因为它把之前匹配的信息都会当做没有用的信息丢弃,而KMP算法正是利用之前匹配的信息,对于每次回溯的位置进行调整。主串匹配到的位置不进行回溯,模式串每次回溯的位置保存在一个next[]的数组中。例如:当主串第i个字符和模式串第j个字符匹配不相等时,模式串下次会用字符位置为next[ j ]的字符和主串i位置的字符进行比较。

next数组的计算(KMP的关键)

当主串S匹配到位置i,模式串T在位置j时,出现不相等。我们这时已知:模式串T在j位置之前的字符和S串在i位置之前的j-1个字符匹配,所以可以据此推断应该回溯的位置:
例如:
主串:S=”abcabacdef…”
模式串:T=”abcabx”
这里写图片描述
这种情况下应该回溯的位置是:
这里写图片描述
通过观察就可以发现,其实每次寻找的就是T串j位置前面的子串’a b c a b’前缀’a b’和后缀’a b’。
这样回溯的位置就仅仅与S串的字符有关,可以根据要匹配的S串事先生成对应的next数组,存储每次匹配到每个位置应该回溯的对应位置。
next数组的推导

  1. j=1时:next[ 1 ] = 0;
  2. j>1时:判断模式串T第j个字符前的子串前缀(前缀最长从第一位到j-2位,前缀最长可以到j-2个字符,即去除前缀等于子串本身的情况)和后缀(即去除后缀等于子串本身的情况)最多有多少位相等,如k位相等,则next[ j ] = k+1。例如’a b c a b x’,j=6时,即子串’a b c a b’,前缀:’a’、’a b’、’a b c’、’a b c a’;后缀:’b’、’a b’、’c a b’、’b c a b’;前缀后缀一样的字符串中最长的有2位,这时next[ j ] = 2+1=3

KMP算法改进

改进也就是针对next数组做出改进改进数组为nextval
例如:
主串:S=”abcabacdef…”
模式串:T=”abcabc”
当字符串S匹配到第六位’a’和T匹配到第六位’c’时,按next数组就要回溯到T串的第三位’c’继续和S串的第六位比较,因为我们发现T串第三位和T串第6位相等,既然第六位已经判断过和S串第六位不相等,那么就没有必要判断一次T串的第三位,我们也就没必要回溯到T串第三位,可以避免重复比较。这时nextval[6]=next[3],即回溯到next数组第三位不相等时应该回溯的位置。
改进nextval数组的推导:(需要依靠next推出)

  1. j=1时:nextval[1]=0;
  2. j>=2时:判断T串第j个字符是否和T串第next[j]个字符相等;不相等时nextval[j]=next[j];相等时nextval[ j ] = nextval[ next[ j ] ]

具体代码

next 数组生成代码

/* 通过计算模式串T,返回next数组,T[0]存储串的长度,串的字符从T[1]开始 */
void get_next(String T,int *next)
{
    int i,j;
    i=1;
    j=0;
    next[1]=0;
    while(i<T[0])
    {
        if(j==0||T[i]==T[j])
        {
            ++i;
            ++j;
            next[i]=j;
        }
        else
            j=next[j];  // 字符不相同时,j值进行回溯
    }
}

数组nextval数组的求取代码

void get_nextval(String T,int *nextval)
{
    int i,j;
    i=1;
    j=0;
    nextval[1]=0;
    while(i<T[0])
    {
        if(j==0 || T[i]==T[j])
        {
            ++i;
            ++j;
            if(T[i]!=T[j])       // 若当前字符与前缀字符不同
                nextval[j] = j;  
            else
                nextval[i] = nextval[j];
        }
        else
            j = nextval[j];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值