KMP算法-字符串匹配

本博客内容参照http://blog.csdn.net/v_july_v/article/details/7041827

Problem

假设现在我们面临这样一个问题:有一个文本串S,和一个模式串P,现在要查找P在S中的位置,怎么查找呢?

如果用暴力匹配的思路,并假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,则有:
  1. 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
  2. 如果失配(即S[i]! = P[j]),令i = i – (j – 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。
    时间复杂度O((n-m+1)m)

KMP solution

下面先直接给出KMP的算法流程(如果感到一点点不适,没关系,坚持下,稍后会有具体步骤及解释,越往后看越会柳暗花明☺):

假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置
– 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
– 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j – next [j] 位。
– 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 – 失配字符对应的next 值,即移动的实际位数为:j – next[j],且此值大于等于1。

很快,你也会意识到next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。
此也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。

Next数组求解

《KMP算法-字符串匹配》
《KMP算法-字符串匹配》
next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。意识到了这一点,你会惊呼原来next 数组的求解竟然如此简单:就是找最大对称长度的前缀后缀,然后整体右移一位,初值赋为-1(当然,你也可以直接计算某个字符对应的next值,就是看这个字符之前的字符串中有多大长度的相同前缀后缀)。

Source code

#include <iostream>
using namespace std;
/* void GetNext(char *p, int *next) { next[0] = -1;//初始化 for (int j = 1; p[j] != '\0'; j++) { int pre = next[j - 1]; if (p[pre] == p[j-1])// next[j] = next[j-1] + 1; else { int k = next[j - 1]; while (k != -1&& p[k] != p[j - 1]) { k = next[k]; if (k == -1) break; } if (k == -1) next[j] = 0; else next[j] = next[k] + 1; } } } */
void GetNext(char *p, int *next)
{
    int len = strlen(p);
    next[0] = -1;
    int k = -1;
    int j = 0;
    while (j < len)
    {
        if (k == -1 || p[k] == p[j])
        {
            k++;
            j++;
            next[j] = k;
        }
        else
            k = next[k];
    }
}

void KMP(char *text, char *p, int *next)
{
    int i = 0, j = 0;
    bool flag = false;
    while (text[i] != '\0')
    {
        if (text[i] == p[j]||j ==-1)//当前字符匹配成功或者第一个字符不匹配,i,j均向后移动
        {
            i++;
            j++;
        }
        else
            j = next[j];//取下一个匹配地址
        if (p[j] == '\0')//匹配成功
        {
            flag = true;
            cout << "Pattern occurs with shift " << i - j << endl;
            if (text[i] == '\0')
                break;
            else
            {
                j = 0;
                i++;
            }
        }
    }
    if (!flag)
        cout << "Not Matched\n";
}

int main()
{
    char *text = new char[100];
    char *p = new char[100];
    int next[100];//next数组
    cout << "Please input source string and objective string\n";
    cin >> text >> p;
    if (*text == '\0' || *p == '\0')
    {
        cout << "Empty string!\n";
        return 0;
    }
    GetNext(p, next);
    KMP(text, p, next);
    return 0;
}
    原文作者:KMP算法
    原文地址: https://blog.csdn.net/lime1991/article/details/48178307
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞