zoukankan      html  css  js  c++  java
  • KMP模板题 P1537

    Description

    给出两个字符串P和T(仅由大写字母构成),请你计算字符串P在T中出现的次数。

    Input

    第一行一个整数n,表示数据组数,每组数据包含两行,第一行是字符串P,第二行是字符串T。

    Output

    对于每组数据输出一个整数,表示P在T中出现的次数。

    Hint

    1<=|P|<=10 000|P|<={T|<=1 000 000

    Solution

    P是模式串,T是主串,定义i,j两个假指针,用i指向主串,用j指向模式串,普通算法是通过strlen得到两个串的长度后,遇到不匹配的地方,i,j都需要回退,时间效率为O(mn),为了提高效率,使得i不回退,只有j这个指针回退,那么就需要求得P这个模式串的最大前缀与后缀,使得当遇到不匹配的地方时j只需要回退到最大前缀的最后一个位置,而i就不需要回退,只需要继续++。实现这个过程就需要一个fail数组用来存放当每个j遇到与i不匹配时,将要回退到什么地方。

    首先因为Input有多组数据,所以每一次程序开始的时候最好进行清零(ql函数)。

    输入T,P两个字符串,在setfail之前用strlen得到两个串的长度,此时用k,j两个假指针指向模式串,当1这个位置不匹配时fail应该回退到0这个位置,将fail[0]初始化为-1,那么当1这个位置不匹配时就会++,此时fail[1]=0,当k与j不匹配且k>=0时,即k还可以回退的时候,每次不匹配k就进行回退直到k,j匹配,而j只需要++,并将fail[j]赋值为k,那么每次i,j不匹配时,j=fail[j],j就会回退到最大前缀的最后一个位置。

    而kmp的过程与setfail的过程非常相似,只是指向的是两个数组,i指向T,j指向P,由于问题求的是P在T中出现的次数,需要用一个cnt来记录出现的次数。每到不匹配的时候j就进行回退(回退只需要j=fail[j]就可以回退),否则i++,j++。每到j全部匹配完一次就将cnt++,然后再次把j回退到fail[j]就可以了。

    注意事项:

    1.输入T和P串时是从0位置开始输入的所以strlen得到的字符长度多了1,循环的时候不需要取等。

    2.在将i和j进行跳动的时候i和j要同时++。

    3.习惯每次输出数据打 。

    4.setfail之后要把j清零。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define maxn 1000001
    using namespace std;
    char T[maxn],P[maxn];
    int fail[maxn];
    int i,j,k,m,n,u,cnt;
    void ql(){
        memset(T,0,sizeof(T));
        memset(P,0,sizeof(P));
        memset(fail,0,sizeof(fail));
        i=j=m=n=cnt=0;
    }
    void init(){
        scanf("%s",P);
        scanf("%s",T);
    }
    void setfail(){
        m=strlen(T);
        n=strlen(P);
        fail[0]=-1;
        k=-1;j=0;
        for(;j<n;){
        while(P[k]!=P[j]&&k>=0){
            k=fail[k];
        }
            k++;
            j++;
            fail[j]=k;
        }
    }
    void kmp(){
        j=0;
        for(int i=0;i<m;){
            while(P[j]!=T[i]&&j>=0){
                j=fail[j];
            }
            i++;
            j++; 
            if(j==n){
                cnt++;
                j=fail[j];
            }
        }
    }
    int main(){
        scanf("%d",&u);
        for(int p=1;p<=u;p++){
            ql();
            init();
            setfail();
            kmp();
            printf("%d
    ",cnt);
        }
        return 0;
    }
    
  • 相关阅读:
    android开发 退出程序
    armeabi和armeabi-v7a引起的问题
    我的博客
    第二章 应用层(一) 应用层概览
    第一章 计算机网络和因特网
    Linux学习笔记——第一篇——Ubuntu安装与操作
    怒学Python——完结篇——I/O
    怒学Python——第四篇——函数与模块
    怒学Python——第三篇——结构控制
    怒学Python——第二篇——类型与运算
  • 原文地址:https://www.cnblogs.com/virtual-north-Illya/p/10044960.html
Copyright © 2011-2022 走看看