AC自动机+fail树+树链求并+树状数组
挺复杂的一道题。
看到这题的第一反应是把S里的串全都丢到AC自动机里,然后对于T中的每一个串在AC自动机上走,走到每一个节点时暴力一直往回跳fail来贡献答案。由于fail指针可能有很多,应该是可以卡到O(n^2)的,过不了吧。
我们发现每一次贡献答案都是沿着fail边往上的,于是用fail树来考虑。每一次一个节点贡献答案相当于该节点到根的路径全都贡献答案。最终贡献答案的形式应当是若干条树链的并都加上1。
转化为做树链的并:按DFS序排序,每个点到根的路径上的所有节点权值+1,相邻两个点的LCA到根的路径上的所有节点权值-1。(对这个方法不理解的可以手动画图,能对这个方法有一个感性的认识)
转化为树链加值的问题,用树链剖分+线段树肯定T。转化为差分,那么一个节点的答案就是子树和了。用树状数组就可以。
运气挺好,写下来没有什么重大的错误, 还是被一些小问题卡了很久。。。毕竟我弱。。。
#include<cstdio>
#include<algorithm>
#define N 100005
#define L 2000005
#define H 23
#define lowbit(_i) (_i&-_i)
using namespace std;
namespace ziqian
{
const int INF = 1<<30;
int n, ecnt, pos[N], last[L], total, dfn[L], dfnn[L], beg[L], end[L], dfn_timer, timer, list[L<<1], log[L<<1], bin[H], dep[L], ST[L<<1][H];
char s[L];
struct edge{int next, to;}e[L];
void addedge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
void dfs(int x)
{
list[++timer] = x;
beg[x] = timer;
dfn[x] = ++dfn_timer;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
dep[y] = dep[x] + 1;
dfs(y);
list[++timer] = x;
}
end[x] = timer;
dfnn[x] = dfn_timer;
}
inline int dep_min(int a, int b){return dep[a]<dep[b] ? a:b;}
void build_ST()
{
dep[0] = INF; bin[0] = 1;
for(int i = 1; i < H; i++)bin[i] = bin[i-1] << 1;
for(int i = 2; i <= timer; i++)log[i] = log[i>>1] + 1;
for(int i = 1; i <= timer; i++)ST[i][0] = list[i];
for(int h = 1; h < H; h++) for(int i = 1; i+bin[h]<=timer; i++)
{
ST[i][h] = dep_min(ST[i][h-1], ST[i+bin[h-1]][h-1]);
}
}
int ask_lca(int a, int b)
{
a = beg[a], b = beg[b];
if(a > b)swap(a, b);
int len = b-a+1;
return dep_min(ST[a][log[len]], ST[b-bin[log[len]]+1][log[len]]);
}
struct BITree
{
int t[L];
void add(int x, int val){while(x <= dfn_timer)t[x] += val, x += lowbit(x);}
int ask(int x){int sum = 0; while(x) sum += t[x], x -= lowbit(x); return sum;}
}BIT;
struct node
{
node *fail, *ch[26];
}mempool[L], *tot, *q[L];
bool cmp(node* a, node* b){return dfn[a-mempool] < dfn[b-mempool];}
struct ACAM
{
node *root;
void init()
{
tot = mempool;
root = ++tot;
}
void insert(int id)
{
node *p = root;
for(int i = 0; s[i]; i++)
{
int c = s[i]-'a';
if(!p->ch[c])p->ch[c] = ++tot;
p = p->ch[c];
}
pos[id] = p - mempool;
}
void build()
{
total = tot - mempool;
int head=0, tail=0;
for(int c = 0; c < 26; c++)
{
if(root->ch[c])q[tail++] = root->ch[c], root->ch[c]->fail = root;
else root->ch[c] = root;
}
for(; head<tail; head++)
{
node *x = q[head];
for(int c = 0; c < 26; c++)
{
if(x->ch[c]) q[tail++] = x->ch[c], x->ch[c]->fail = x->fail->ch[c];
else x->ch[c] = x->fail->ch[c];
}
}
for(int i = 0; i < tail; i++)
addedge(q[i]->fail - mempool, q[i] - mempool);
}
int solve()
{
node *p = root;
int tail = 0;
for(int i = 0; s[i]; i++)
{
int c = s[i] - 'a';
p = p->ch[c];
if(p != root)q[++tail] = p;
}
return tail;
}
}AC;
void main()
{
AC.init();
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%s",s);
AC.insert(i);
}
AC.build();
dfs(1);
build_ST();
int Q; scanf("%d",&Q);
for(; Q--; )
{
int opt, x;
scanf("%d",&opt);
if(opt == 1)
{
scanf("%s",s);
int tail = AC.solve();
sort(q+1, q+1+tail, cmp);
for(int i = 1; i <= tail; i++)
{
BIT.add(dfn[q[i]-mempool], 1);
if(i != 1) BIT.add(dfn[ask_lca(q[i] - mempool, q[i-1]- mempool)], -1);
}
}
else
{
scanf("%d",&x);
int tmp1 = BIT.ask(dfn[pos[x]]-1), tmp2 = BIT.ask(dfnn[pos[x]]);
printf("%d\n",tmp2 - tmp1);
}
}
}
}
int main()
{
ziqian::main();
}