题目大意:
就是给你 n 个压缩形式的字符串 n <= 2500, 然后每个字符串解压前和解压后的长度都不超过1100, 然后给出一个长串,也是压缩格式,输入保证其长度在压缩和解压之后长度都不超过5100000,现在对于 t 组测试数据,每组输出给出的n个字符串在当组最后给的长串中出现了几次,其中如果串A, B都出现了,且 A是B的子串,则不计A
大致思路:
明显的AC自动机的题......但是模拟做的时候还是TLE了很多遍,后来发现一个地方的回溯可以不用回溯...才终于过掉了
首先对于每组数据的n个串建立一颗Trie树,算出fail指针(AC自动机的转移),并且用end[num]数组记录以结点num作为结尾的串的编号是多少。
然后遍历文本串,用flag数组记录n个模板串种有哪些串是出现过的,然后对于出现过的串在AC自动机上进行转移找出其子串,见子串的flag标记去掉,最后统计标记有flag的串的个数即可。
代码如下:
Result : Accepted Memory : 28620 KB Time : 360 ms
/*
* Author: Gatevin
* Created Time: 2014/11/15 13:13:23
* File Name: AC.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
int n;
char ts[5100005];
char all[5100005];
char s[2505][1105];
int next[5100005][26], fail[5100005];
int end[5100005];
bool flag[2505];
int L, root, ans;
int newnode()//新节点
{
for(int i = 0; i < 26; i++)
next[L][i] = -1;
end[L++] = 0;
return L - 1;
}
void init()//初始化Trie树
{
L = 0;
root = newnode();
memset(flag, 0, sizeof(flag));
return;
}
void insert(char* t, int id)//插入模板串
{
int now = root;
for(; *t; t++)
{
if(next[now][*t - 'A'] == -1)
next[now][*t - 'A'] = newnode();
now = next[now][*t - 'A'];
}
end[now] = id;//记录模板串编号
return;
}
void build()//计算fail数组
{
queue <int> Q;
fail[root] = root;
Q.push(root);
while(!Q.empty())
{
int now = Q.front();
Q.pop();
for(int i = 0; i < 26; i++)
if(next[now][i] == -1)
next[now][i] = now == root ? root : next[fail[now]][i];
else
{
fail[next[now][i]] = now == root ? root : next[fail[now]][i];
Q.push(next[now][i]);
}
}
return;
}
void sea(int id)//对于出现了的模板串,寻找其子串
{
int now = root;
char *t = &s[id][0];
for(; *t; t++)
{
now = next[now][*t - 'A'];
int tmp = now;
if(*(t + 1) == '\0')
{
tmp = fail[tmp];
}
while(tmp != root)
{
if(end[tmp] && flag[end[tmp]])
{
flag[end[tmp]] = 0;
}
tmp = fail[tmp];
}
}
return;
}
int query(char* t)//查找出现的模板串
{
int now = root;
ans = 0;
for(; *t; t++)
{
now = next[now][*t - 'A'];
//int tmp = now;
if(end[now])//这里不用while(tmp != root)标记所有子串,减少计算量
{
flag[end[now]] = 1;
}
}
for(int i = 1; i <= n; i++)
{
if(flag[i])
{
sea(i);
}
}
for(int i = 1; i <= n; i++)
{
if(flag[i]) ans++;
}
return ans;
}
void dcom(char * s)//解压还原字符串
{
char* t = &ts[0];
int now = 0;
for(; *t; t++)
{
if(*t == '[')
{
t++;
int tmp = 0;
while(*t <= '9' && *t >= '0')
{
tmp = tmp*10 + *t - '0';
t++;
}
while(tmp--)
{
s[now++] = *t;
}
t++;
}
else
{
s[now++] = *t;
}
}
s[now] = '\0';
return;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
init();
for(int i = 1; i <= n; i++)
{
scanf("%s", ts);
dcom(s[i]);
insert(s[i], i);
}
build();
scanf("%s", ts);
dcom(all);
printf("%d\n", query(all));
}
return 0;
}