hdu 5384 Danganronpa (字典树/AC自动机)

给出若干仅由小写字母构成的A串、B串,问对于每一个A串,所有的B串在该串中出现的次数。


标程是用AC自动机做的。蒟蒻暂时还不太会AC自动机。

然后看到有用字典树直接做的,就学习了一下。

求B在A中总共出现的次数,其实就是求A的所有子串中含有B的个数。而一个串的所有子串可以表示为它所有后缀的前缀。因此,可以把B[i]插入字典树中,对于每一个A[i],查询它的每一个后缀的所有前缀在字典树中出现了几次,累加起来就是答案。


用C++交RE,用G++就过了。


#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<set>
#include<algorithm>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cstring>
#include<vector>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef __int64 LI;
typedef unsigned __int64 uLI;
typedef unsigned int uI;
typedef double db;
#define maxn 100005
#define inf 0x3f3f3f3f
int n,m;
char A[100001][10005];
char B[100005];
struct Trie{
    int tree[100005][26],val[100005],cnt;//结点编号,从1开始
    Trie(){
        init();
    }
    void init()
    {
        cnt=1;
        memset(tree,0,sizeof(tree));
    }
    void insert(char s[]){
        int p=0,l=strlen(s);
        for(int i=0;i<l;++i){
            int a=s[i]-'a';
            if(!tree[p][a]){
                val[cnt]=0;
                tree[p][a]=cnt++;
            }
            p=tree[p][a];//指向下一结点编号
        }
        ++val[p];
    }

    int querry(char s[]){
        int p=0,l=strlen(s),ans=0;
        for(int i=0;i<l;++i)
        {
            int a=s[i]-'a';
            if(!tree[p][a]) return ans;
            p=tree[p][a];
            ans+=val[p];
        }
        return ans;
    }
}T;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        T.init();
        for(int i=0;i<n;++i) scanf("%s",A[i]);
        LI ans;
        for(int i=0;i<m;++i) {scanf("%s",B);T.insert(B);}
        for(int i=0;i<n;++i)
        {
            ans=0;
            int l=strlen(A[i]);
            for(int j=0;j<l;++j) ans+=T.querry(A[i]+j);
            printf("%I64d\n",ans);
        }
    }
    return 0;
}


你可能感兴趣的:(hdu 5384 Danganronpa (字典树/AC自动机))