zoukankan      html  css  js  c++  java
  • 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; }

                                                                                                                                                          

     
  • 相关阅读:
    if判断语句和循环语句
    列表,元祖,字典的详细概述
    day10
    day09
    day08
    java---基本程序设计总结
    day07
    day06
    day05
    day04
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3237271.html
Copyright © 2011-2022 走看看