luogu3375 【模板】KMP字符串匹配

题目大意:给出两个字符串s和p,其中p为s的子串,求出p在s中所有出现的位置。

p[0, i-1]中,若存在最长的相等的后缀与前缀,则next[i]为那个前缀的末尾位置的下一个位置。匹配时,如果p[i]!=s[j],因为p[0,i-1]中的后缀已经匹配成功,则把与其相等的前缀放在那个后缀的位置,也会匹配成功。所以让i=next[i]继续匹配即可。

求next数组,要用递推。每次while循环体内解决的问题是:已知next[j]==k,求next[j+1]。如果p[j]==p[k],对于j+1来说,前缀的长度和后缀的长度都加1,还相等,把next[j+1]设成k+1即可;否则,令k=next[k],根据定义,p[0,next[k]-1]该前缀1能在p[0,k-1]中找到与其相等的后缀2,该p[0,k-1]整体该前缀3会在p[0,j-1]中找到与其相等的后缀4。因为1=2,2是3后缀,3=4,故1与4的后缀相等。就这样不断循环即可。

#include <cstdio>
#include <cstring>
#include <cassert>
using namespace std;

const int MAX_STR = 1000010;

void SetNext(char *p, int *next)
{
    int len = strlen(p);
    int j = 0, k = -1;
    next[0] = -1;
    while (j < len)
    {
        if (k == -1 || p[j] == p[k])
        {
            /*if (p[j + 1] == p[k + 1])
                next[++j] = next[++k];
            else*/
                next[++j] = ++k;
        }
        else
            k = next[k];
    }
}

void GetP(char *s, char *p, int *next, int *ans)
{
    int sLen = strlen(s), pLen = strlen(p), sPos = 0, pPos = 0, ansCnt = 0;
    while (sPos < sLen)
    {
        if (pPos == -1 || s[sPos] == p[pPos])
        {
            sPos++;
            pPos++;
        }
        else
            pPos = next[pPos];
        if (pPos == pLen)
        {
            ans[ansCnt++] = sPos - pPos + 1;
            pPos = 0;
            sPos = sPos - pLen + 2;
        }
    }
}

int main()
{
#ifdef _DEBUG
    freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
    static char s[MAX_STR], p[MAX_STR];
    static int next[MAX_STR], ans[MAX_STR], pLen;
    scanf("%s%s", s, p);
    pLen = strlen(p);
    SetNext(p, next);
    GetP(s, p, next, ans);
    for (int i = 0; ans[i]; i++)
        printf("%d\n", ans[i]);
    for (int i = 1; i < pLen + 1; i++)
        printf("%d ", next[i]);
    printf("\n");
    return 0;
}
View Code

注意:

  • 是k==-1(pPos==-1),而不是next[k]==-1(next[pPos]==-1)。
  • 设置next时,k初值为-1,而不是0(看next的定义)。

优化:若p[next[j]]==p[j],则实际匹配时,我们还要求next[next[j]]。因此,把代码中注释部分加上即可。

 

posted @ 2018-02-19 12:01  headboy2002  阅读(110)  评论(0编辑  收藏  举报