关于kmp的一点个人理解 poj3461 Oulipo (kmp模板题)

这个题就是先给子串,然后给母串,问子串在母串中出现的次数。

Oulipo

Time Limit: 1000 ms / Memory Limit: 32768 kb

Description

The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e'. He was a member of the Oulipo group. A quote from the book:

Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…

Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive 'T's is not unusual. And they never use spaces.

So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {'A', 'B', 'C', …, 'Z'} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap.
 

Input

The first line of the input file contains a single number: the number of test cases to follow. Each test case has the following format:

One line with the word W, a string over {'A', 'B', 'C', …, 'Z'}, with 1 ≤ |W| ≤ 10,000 (here |W| denotes the length of the string W).
One line with the text T, a string over {'A', 'B', 'C', …, 'Z'}, with |W| ≤ |T| ≤ 1,000,000.

Output

For every test case in the input file, the output should contain a single number, on a single line: the number of occurrences of the word W in the text T.

3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
1
3
0
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10000005;
int knext[maxn];
char s[maxn],t[maxn];
int slen,tlen;
void getnext()
{
    int j,k;
    j=0;k=-1;knext[0]=-1;
    while(j<tlen)
    {
        if(k==-1||t[j]==t[k])
        {
            knext[++j]=++k;

        }
        else
            k=knext[k];
    }
}
int count_kmp()
{
    int i=0,k=0;
    int ans=0;
    int j=0;
    if(slen==1&&tlen==1)
    {
        if(s[0]==t[0])
            return 1;
        else
            return 0;
    }
    getnext();
    for(i=0;i<slen;++i)
    {
        while(j>0&&s[i]!=t[j])
            j=knext[j];
        if(s[i]==t[j])
            j++;
        if(j==tlen)
        {
            ans++;
            j=knext[j];
        }
    }
    return ans;
}
int main(void)
{
    int a;
  scanf("%d",&a);
    while(a--)
    {
        scanf("%s",t);
        scanf("%s",s);
        slen=strlen(s);
        tlen=strlen(t);
        printf("%d\n",count_kmp());
    }
}

解释一下,next数组相当于在子串中找到相同的部分,使得已经匹配过的部分不需要再一次匹配。

比如说子串adsccadc  母串adsccadsccadc

当匹配到

  adsccadsccadc

  adsccadc

这时候c与s不相同 按照朴素算法应该是

  adsccadsccadc

    adsccadc

这样再进行匹配

但是我们可以看到 子串中的最后三位adc的ad与母串中的ad已经匹配了 又因为子串一开始就是ad开头的所以无需再进行ad的匹配,直接

adsccadsccadc

         adsccadc

这里的移动必须是后缀和前缀相同的(后缀就是从中间的某一位到最后的子串,前缀就是从第一位到最后一位之前的某一位的子串),比如子串中前缀ad(第一个ad)与后缀中ad(第二个ad)是相同的。但是如果后缀往前加一位比如cad这时候如果cad匹配,那么ad是显然不匹配的

adsccadsccadc

       adsccadc

所以next数组就是寻找相同的前缀与后缀

这里的next数组如果前缀第一位和后缀第一位相同或者不同,next【k】都是0

但是

前缀第一位和后缀第一位相同那么next[k]=0;

这时候再看第二位,如果和前缀第二位相同,那么在之前的字符的next[k]的大小上+1

比如从子串第二个ad开始的后缀 a与a相同 next[5]=0 再往下看 d与d相同 那么next[6]=next[5]+1

再往下看s与c不同 那么next[7]=0

这个意思就是说第二个adc的c的next是2 就是说前面的都匹配了 但是c开始不匹配了 就转到子串下标为2的地方继续匹配。

子串adsccadcada的next数组为

-1,0,0,0,0,0,1,2,0,1,2

猜你喜欢

转载自blog.csdn.net/qq_41780519/article/details/81408075