Lightoj 1427 Substring Frequency (II) 【AC自动机(静态数组)】

 

A string is a finite sequence of symbols that are chosen from an alphabet. In this problem you are given a string T and n queries each with a string Pi, where the strings contain lower case English alphabets only. You have to find the number of times Pi occurs as a substring of T.

Input

Input starts with an integer T (≤ 10), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 500). The next line contains the string T (1 ≤ |T| ≤ 106). Each of the next n lines contains a string Pi (1 ≤ |Pi| ≤ 500).

Output

For each case, print the case number in a single line first. Then for each string Pi, report the number of times it occurs as a substring of T in a single line.

Sample Input

Output for Sample Input

2

5

ababacbabc

aba

ba

ac

a

abc

3

lightoj

oj

light

lit

Case 1:

2

3

1

4

1

Case 2:

1

1

0

 

是AC自动机的模板题,这里用了静态数组,用动态会超时,思路就不讲了,代码复制上来以后当模板看,我不是很会数组的AC自动机,但是没办法,指针的耗时要多一些;

 

#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<cstdio>

using namespace std;

#define Maxn 1000010
#define maxn 300100
#define maxm 510

struct Trie {
    int node,cnt;
    int child[maxn][30],fail[maxn],val[maxn],vis[maxm],ans[maxn];
    void init () {
        memset(vis,0,sizeof(vis));
        memset(ans,0,sizeof(ans));
        cnt = 0;
        node = -1;
        newnode ();
    }
    int newnode () {
        memset(child[++node],0,sizeof(child[0]));
        fail[node] = val[node] = 0;
        return node;
    }

    void insert_ (char *ptr, int x) {
        int len = strlen(ptr),index,cur = 0;
        for (int i = 0; i < len; ++i) {
            index = ptr[i]-'a';
            if(!child[cur][index]) {
                child[cur][index] = newnode ();
            }
            cur = child[cur][index];
        }
        val[cur] = 1;
        vis[cnt++] = cur; // 用一个数组来记录代表子字符串的节点值,因为要判断的子字符串可能有重复
    }

    void getfail () {
        queue<int> qu; int cur;
        for (int i = 0; i < 30; ++i) {
            if(child[0][i]) qu.push(child[0][i]);
        }
        while (!qu.empty()) {
            cur = qu.front(); qu.pop();
            for (int i = 0; i < 30; ++i) {
                if(child[cur][i]) {
                    fail[child[cur][i]] = child[fail[cur]][i];
                    qu.push(child[cur][i]);
                } else child[cur][i] = child[fail[cur]][i];
                // 解释一下上面这个else的代码,如果再匹配的时候,当前节点如果发现子节点没有能和主字符串
                //对应字符匹配的时候,会用fail指针去访问对应fail指针节点的子节点,这里是把当前节点
                //child[cur][i]直接指向fail对应指针节点的子节点,当用到query的时候可以不用fail一一访问
                //就可以直接找到目标节点;这个地方的优化好像叫,,,Tire图?不清楚,但这是一个不错的优化
            }
        }
    }

    void query (char *str) {
        int len = strlen(str),cur = 0,index,p;
        for (int i = 0; i < len; ++i) {
            index = str[i]-'a';
            cur = child[cur][index];
            p = cur;
            while (p && val[p] >= 0) {
                if(val[p] == 1) ans[p]++; // 再用另一个记录对应的节点值所对应匹配成功的个数
                p = fail[p];
            }
        }
    }

} AC;

char pt[maxm],s[Maxn];

int main(void)
{
    int t,n;
    scanf("%d",&t);
    for (int i = 1; i <= t; ++i) {
        scanf("%d",&n); scanf(" %s",s);
        AC.init();
        for (int j = 1; j <= n; ++j) {
            scanf(" %s",pt);
            AC.insert_(pt,j);
        }
        AC.getfail(); AC.query(s);
        printf("Case %d:\n",i);
        for (int j = 0; j < n; ++j) {  //vis保证顺序,ans这是按顺序输出的答案
            printf("%d\n",AC.ans[AC.vis[j]]);
        }
    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值