题目大意:给出一些字符串,给出一些询问,每次问当前串在源串中的几个中出现过。
思路:将所有源串建立广义后缀自动机。每次新的一个串的时候,把last清成root,往里面加的时候,如果last指针往下走的时候已经有节点了,就需要拓展一个新的节点出来,否则就不满足广义后缀自动机的性质。此外,每一个节点代表的不一定是一个串,可能代表的是多个串的子串,所以要在每个点后面挂链,来表示这个节点是属于哪几个串中的子串。后面的事情就比较简单了,把后缀树建立出来,弄出DFS序,离线处所有询问,变成在一段序列中出现过多少不同的数字,弄一个树状数组维护一下。
其实和喵星球上的点名是差不多的,但是那个题我用的后缀数组暴力的,这个题会T。
还有这个题坑爹的卡内存啊,字符集太大,直接建立肯定mle,用map的话就不能用指针,我这还是第一次用数组写啊。。DT死了。。。
CODE:
//#define _CRT_SECURE_NO_DEPRECATE
#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 200010
using namespace std;
struct Graph{
int head[MAX],total;
int next[MAX],aim[MAX];
void Add(int x,int y) {
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
}G,repre;
struct Ask{
pair<int,int> interval;
int id;
Ask(pair<int,int> _,int __):interval(_),id(__) {}
Ask() {}
bool operator <(const Ask &a)const {
return interval.second < a.interval.second;
}
}ask[MAX];
int len[MAX],father[MAX];
map<int,int> tranc[MAX];
int cnt,root,last;
inline int NewNode(int _)
{
len[++cnt] = _;
return cnt;
}
void Initialize()
{
root = last = NewNode(0);
}
inline void Add(int c,int i)
{
int p = tranc[last][c];
if(p) {
if(len[p] == len[last] + 1) last = p;
else {
int rep = NewNode(len[last] + 1);
father[rep] = father[p];
father[p] = rep;
tranc[rep] = tranc[p];
for(int temp = last; temp && tranc[temp][c] == p; temp = father[temp])
tranc[temp][c] = rep;
last = rep;
}
}
else {
int np = NewNode(len[last] + 1);
for(p = last; p && !tranc[p][c]; p = father[p])
tranc[p][c] = np;
if(!p) father[np] = root;
else {
int q = tranc[p][c];
if(len[q] == len[p] + 1) father[np] = q;
else {
int nq = NewNode(len[p] + 1);
father[nq] = father[q];
father[q] = father[np] = nq;
tranc[nq] = tranc[q];
for(; p && tranc[p][c] == q; p = father[p])
tranc[p][c] = nq;
}
}
last = np;
}
repre.Add(last,i);
}
pair<int,int> subtree[MAX];
int seq[MAX],_clock;
void DFS(int x)
{
seq[++_clock] = x;
subtree[x].first = _clock;
for(int i = G.head[x]; i; i = G.next[i])
DFS(G.aim[i]);
subtree[x].second = _clock;
}
char s[MAX << 1];
inline pair<int,int> Find()
{
int now = root,length = strlen(s);
for(int i = 0; i < length; ++i) {
if(!tranc[now][s[i]]) return make_pair(-1,-1);
now = tranc[now][s[i]];
}
return subtree[now];
}
int fenwick[MAX];
inline void Fix(int x,int c)
{
for(; x <= cnt; x += x&-x)
fenwick[x] += c;
}
inline int GetSum(int x)
{
int re = 0;
for(; x; x -= x&-x)
re += fenwick[x];
return re;
}
int strings,asks;
int last_add[MAX],ans[MAX];
int main()
{
Initialize();
scanf("%d%d",&strings,&asks);
for(int i = 1; i <= strings; ++i) {
scanf("%s",s);
int length = strlen(s);
last = root;
for(int j = 0; j < length; ++j)
Add(s[j],i);
}
for(int i = 1; i <= cnt; ++i)
if(father[i])
G.Add(father[i],i);
DFS(1);
for(int i = 1; i <= asks; ++i) {
scanf("%s",s);
ask[i] = Ask(Find(),i);
}
sort(ask + 1,ask + asks + 1);
int now = 1;
while(ask[now].interval.first == -1 && now <= asks) ++now;
for(int i = 1; i <= cnt; ++i) {
for(int j = repre.head[seq[i]]; j; j = repre.next[j]) {
int col = repre.aim[j];
Fix(i,1);
if(last_add[col])
Fix(last_add[col],-1);
last_add[col] = i;
}
for(; ask[now].interval.second == i; ++now)
ans[ask[now].id] = GetSum(ask[now].interval.second) - GetSum(ask[now].interval.first - 1);
}
for(int i = 1; i <= asks; ++i)
printf("%d\n",ans[i]);
return 0;
}