poj_3461 KMP算法解析

A - Oulipo
Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u
Submit Status

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.

Sample Input

3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN

Sample Output

1
3
0

 纯KMP算法,KMP确实是神算法。。一开始我还没弄懂就开始敲,居然把长字串给求next值了,KMP其妙是在其求的出现的重复字串的next值,当失配时,只需要回溯到第一个重复字串(后面的都是重复前面的字串)的相应失配位置,再开始匹配就行,不需要从头开始。。这样就把一个嵌套的循环活生生的改成了一个O(n)的时间复杂度。

KMP的实现主要是两步

1.求next数组的值。

我的前提是字串读取为从0开始到len-1位置。

先定义next[0]为0(也有定义为-1的,我的习惯是0)。

从1到len-1循环,

定义i为循环变量,顺便定义j为next数组的循环变量(j=0初始),当s[i]==s[j]时,说明出现重复字串,则j++;

如果不相等,则说明此处失配,但是!!!注意:失配不代表当前next值就为0,需回溯到该失配处对应的上一处重复点,

即while(j>0&&s[i]!=s[j])j=next[j-1];(这里需要着重说明,因为刚刚坑也坑在这里,因为此时j处失配,因此通过next[j-1]回溯到之前,但正好由于数组下标比实际数位小1,因此得到的next[j-1]值正好就是失配点对应的回溯点,不用再+1了,当然如果一直失配下去,j最终为0)

要是比较难理解这个失配点的回溯点是怎么回事,我们看个例子:

ID 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  
str a b a b c a b a b a b a b c a b a b  
Next 0 0 1 2 0 1 2 3 4 3 4 3 4 5 7 7 8 9  

我们可以看到,着重注意一下下标为9的点,此时s[i=9]!=s[j=4] 则,找回溯点 j=next[j-1=3]=2,即失配前一个字符对应的回溯点告诉我们 失配点的回溯点的下标为2,则此时 s[j=2]==s[i=9],即匹配成功,说明回溯过去,找到了失配点的一个配对点。但注意,一旦配对成功,(有一种情况是回溯过去,找不到配对点,j直接=0,之后不做处理),但如果配对成功了,说明相对于j=2,该点是j=2之后的又一配对点,所以j++,使得j要=3;故此时next[9]=3;

2.匹配过程

1.匹配是从另一个长串开始循环(定义循环变量为i,长串为ls),另一个循环变量q=0则负责s。

初始i s均为0,若ls[i]==s[q] 则 :q++,i++;

如果不等,则s需要回答其失配点对应的回溯点,当然如果q==0,则没有回溯点可言,故直接i++;否则 q=next[q-1](为什么是q-1和上述相同);

。。。。此处进行很多次循环;

当q==len;说明此时已经匹配完成,ans++;(说明匹配到一个字串) 并且 q=next[q-1](原理相同,将q回到末尾点的回溯点的下一点,与ls[i](此时i已经递增一位)继续进行匹配)。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char w[10005];
char t[1000005];
int next[10005];
int ans;
void kmp_1()//求next数组过程
{
    next[0]=0;
    int j=0;
    for (int i=1; w[i]!=''; i++)//用w[i]!='',就不用测算w的长度,减少时间
    {
        if (w[i]==w[j])
        {
            j++;
        }
        else
{ while (j>0&&w[i]!=w[j]) j=next[j-1];
if (w[i]==w[j]) //确实找到了匹配的回溯点,说明next值可以+1了.
j++;
} next[i]
=j; } } void kmp_2()//匹配过程 { int k,q; ans=0; for (k=0,q=0; t[k]!='';) { if (t[k]==w[q]) { q++; k++; } else { if (q==0) k++; else q=next[q-1]; } if (w[q]=='') { ans++; q=next[q-1]; } } } int main() { int tt; scanf("%d",&tt); getchar(); while (tt--) { memset(next,0,sizeof next); gets(w); gets(t); int lenw; //lenw=strlen(w); kmp_1(); kmp_2(); printf("%d ",ans); } return 0; }

                                                                                                                                                      

 
原文地址:https://www.cnblogs.com/kkrisen/p/3237271.html