HDU 1686 :Oulipo

KMP题
不过这次是求单词在文本中出现的次数

一种很容易想到的方法:当单词与文本匹配成功后,文本退回到与单词匹配成功的第一个字符的后一个字符,单词退回到第一个字符,再重新进行匹配,这样显然会超时

其实单词与文本匹配成功后,文本并不需要退回,我们可以这样认为:

当单词与文本匹配成功后,我们假设单词位置m上存在字符(其实是不存在的,因为单词字符的位置范围 [0,m-1]),那么此时单词位置 [0,m-1]上 的字符都与文本匹配成功了,所以文本继续向后移动一个字符到位置a,单词也继续向后移动一个字符到位置m,显然单词不存在位置m这个字符,所以我们认为这次匹配失败,所以文本a位置字符应该跟单词 fail[m] 位置字符进行匹配,这样就满足题目的时间限制了

具体看代码:

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

const int maxn=10000+100;

char ch[maxn*100],ph[maxn];
int fail[maxn];
int n,m;

inline void getFail(){

    fail[0]=fail[1]=0;
    for(int i=1;i<m;i++){

        int j=fail[i];
        while(j && ph[i]!=ph[j]) j=fail[j];
        fail[i+1]= (ph[i]==ph[j])?j+1:0;
    }
}

inline int match(){

    int j=0;
    int cnt=0;
    for(int i=0;i<n;i++){

        while(j && ch[i]!=ph[j]) j=fail[j];
        if(ch[i]==ph[j]) j++;
        if(j==m){

            cnt++;
            j=fail[j];
        }
    }
    return cnt;
}

int main(){

    int T;
    scanf("%d",&T);
    getchar();
    while(T--){

        scanf("%s",ph); m=strlen(ph);
        scanf("%s",ch); n=strlen(ch);
        getFail();
        printf("%d\n",match());
    }
} 

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/81176092