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;
}