感觉KMP很简单就没练过,用的时候太不熟练,写个博客熟悉熟悉。

其实主要就是两部分,一个是确定next数组,一个是利用next找子串,这两部分大致思路很简单,有点异曲同工。

具体细节代码中注释已经说的很清楚了,不再赘述。

#include<bits/stdc++.h>
using namespace std;

vector<int> getNext(const string &pattern)
{
    int m = pattern.size(); // 既可以用来初始化也可以作为循环出口
    vector<int> next(m);
    next.push_back(0); // 一个字符无法匹配默认为0
    int i=1; // 后缀匹配到的字符位置(只增不减)
    int p=0; // 记录当前最大前缀(末)位置(其实也就是p=next[0])
    while(i<m)
    {
        if(next[p] == next[i])
        {
            p++; // 缀数增加,要推进主串,同时构建next
            next.push_back(p);
            i++; // 向后推进
        }
        else // 后缀断连
        {
            if(p == 0) // 没有回退空间(找不到更小前后缀),保持缀数不变,继续推进
            {
                i++;
            }
            else
            {
                p = next[p-1]; // 前后缀长度回退,循环继续
            }
        }
    }
    return next;
}

int main()
{
    string text;// 原串
    string pattern;// 要找的目标字符串
    
    cin >> text;
    cin >> pattern;

    int n=text.size();
    int m=pattern.size();
    if(n < m) return 0; //长度不符合标准
    
    vector<int> next = getNext(text);
    
    // 这里只处理查找到第一个匹配串的位置,如果多个匹配点,重置变量(p),继续匹配不间断输出就行
    
    int j=0; // pattern(副)串匹配到的位置
    int i=0; // text(主)串匹配到的位置
    while(j<m)
    {
        if(pattern[j] == text[i])
        {
            j++;
            i++;
        }
        else
        {
            if(j==0) // 无法回退
            {
                i++; // 向前推进
            }
            else
            {
                j = next[j-1];
            }
        }
    }
    // 这时i位于找到的子串所在位置后面一个
    printf("第一个匹配的子串位置是[%d,%d]\n",i-m,i-1); // 输出所在区间
    return 0;
}