KMP算法

KMP算法

问题:

在字符串匹配问题中,给出两个字符串,文本串test,模式串pattern,如何判断模式串是否是文本串的一个子串

KMP算法核心:

  1. 模式串失配后,不是从下一个字符开始重新匹配,而是利用已有的信息,跳过不可能成功匹配的位置,减少匹配次数,达到快速匹配的目的;
  2. 失配之后具体要跳过多少位置,可以提前用模式串计算出来,计算出来的结果就存在next[]数组中;
  3. next[j] = k;
    代表的是模式串的子串(0 ~ j-1)的前缀和后缀相同的最大长度;
    其中前缀是指:除最后一个字符外,字符串的所有头部子串;
    后缀是指:除第一个字符外,字符串的所有尾部子串;
    因此 k < j 恒成立;

KMP算法过程:

设test串指针为 i, pattern串指针为 j:
此时部分匹配:
“test[i-j] ~ test[i-1]” == “pattern[0] ~ pattern[j-1]”,test[i] != pattern[j];且 next[j] == k;
由next数组定义知:
“pattern[0] ~ pattern[k-1]” == “pattern[j-k] ~ pattern[j-1]”
所以我们接下来只用匹配test[i] 与 pattern[k] 即可,避免了主串的回溯;
代码形式即为:j = next[j];

KMP主体代码:

const int MAXN = 10000;
int KMP(string test, string pattern)
{
    int next[MAXN];
    GetNext(next,pattern);
    int i = 0, j = 0;
    while(i < test.size() && j < pattern.size())
    {
        if(j == -1 || test[i] == pattern[j])//回溯到起点状态,从头匹配;或者当前字符匹配成功,匹配下一个字符
        {
            i ++;
            j ++;
        }
        else
        {
            j = next[j];//当前字符串匹配失败,移动模式串指针
        }
    }
    if(j == m) //匹配成功
    {
        return i-j+1;//返回第一个匹配串下标
    }
    else return -1;//匹配失败
}

next[]数组的求解:

由上面的主代码可知,现在我们面临的问题就是对next数组的求解;

具体求解过程:
next数组的求解具有很强的规律性以及递推关系;
在已知next[j]的情况下,求解next[j+1]:

  1. 设next[j]的值为k,说明在长为j的子串中,0 ~ k-1 的头部子串和 j-k ~ j-1 的尾部子串完全相同;
  2. 现在考虑长为 j+1的子串:
    a. if ( patern[k] == pattern[j])
    即pattern中第k+1个字符与第j+1个字符相同,那么显然next[j+1] = next[j]+1;
    b. if (即patern[k] != pattrtn[j])
    即pattern中第k+1个字符与第j+1个字符不同 ;
    我们可以把这种情况看做一个pattern串与另一个pattern串匹配,出现了失配的现象,将 j 视为主串指针,k 视为目标串指针,则由KMP主体算法的演示过程可知,对k进行回溯而j不变;
    即有: k = next[k] ;(next[k] 必定小于k)

Next数组求解代码

void GetNext(int next[], string pattern)//创建next表,本质上使用pattern自己与自己匹配来获得next数组
{
    next[0] = -1;//初始化
    int k = -1, j = 0;
    
    while(j < pattern.size())//对每个子串求出其对应的next值
    {
        if(k == -1 || pattern[j] == pattern[i])
        {
            k ++;
            j ++;
            next[j] = k;
        }
        else
        {
            k = next[k];//next[i]是小于i的
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值