hdu6153 poj3336强化版kmp+线性dp

发现很早以前用exkmp做过一次,但是对这题来说只要将两个串翻转一下即可转换成s2的所有前缀出现的问题

/*
给出s1,s2,求s2的每个后缀在s1中出现的次数
ans = sum{后缀长度*出现次数} 
思路:把数组倒过来,求s2的nxt数组 

cnt[i]当匹配到s2的第i位可以对答案做的贡献 
cnt[i]=当前与s1第i个字符配对的s2的前缀长度+cnt[nxt[j]] 
*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define mod 1000000007
#define maxn 1000005
#define ll long long

char s1[maxn],s2[maxn];
int nxt[maxn],m,cnt[maxn],ans; 
void reserve(char *s){
    int len=strlen(s);
    int i=0,j=len-1;
    while(i<j){
        swap(s[i],s[j]);
        ++i,--j;
    }
}
void kmp_pre(char *s){
    memset(nxt,0,sizeof nxt);
    int m=strlen(s);
    int i,j;
    i=0,j=nxt[0]=-1;
    while(i<m){
        while(j!=-1 && s[i]!=s[j]) j=nxt[j];
        nxt[++i]=++j;
    }
    for(int i=1;i<=m;i++)
        cnt[i]=(i+cnt[nxt[i]])%mod;
}
void kmp(){
    int n=strlen(s1),m=strlen(s2);
    int i=0,j=0;
    while(i<n){
        while(j!=-1 && s1[i]!=s2[j])
            j=nxt[j];
        ++i,++j;
        ans=(ans+cnt[j])%mod; 
        if(j==m)j=nxt[j];
    }
} 
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        memset(cnt,0,sizeof cnt);
        scanf("%s%s",s1,s2);
        
        reserve(s1);
        reserve(s2);
        kmp_pre(s2);
        ans=0;
        kmp();
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自www.cnblogs.com/zsben991126/p/10274670.html