BZOJ 3881:Divljak
题目传送门
PS:万恶的权限题啊,为什么内存就这么小呢?MLE了无数次。。
【问题描述】
Description
Alice有n个字符串S_1,S_2…S_n,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串S_x。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。
Input
第1行,一个数n;
接下来n行,每行一个字符串表示S_i;
下一行,一个数q;
接下来q行,每行一个操作,格式见题目描述。
Output
对于每一个Alice的询问,帮Bob输出答案。
Sample Input
3
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3
Sample Output
1
2
1
【数据范围】
1 <= n,q <= 100000;
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000;
字符串都由小写英文字母组成。
【解题思路】
因为集合T是只增不减的。
对S建AC自动机,构出fail树。
f[i]表示当前集合T内有多少个串包含Si(只增不减)。
每次加入新的串x,就更新f。
让x在AC自动机上跑。
得到的是一堆树链q[],因为匹配失败会跳fail。
那么,由于每个S即使被包含,也只可被算一次。
树链的并就是需要的答案。
记g[i]为点i到root的路径
将q[]按时间戳dfn从小到大排序。
然后加上g [ q [ i ] ],减去g [ lca ( q [ i-1 ] , q [ i ] ) ]。
为什么这样是正确的?
画个图就可以证明,因为是按时间戳从小到大排的序。
Si被包含的个数,其实就等同于Si的子树和(fail树)。
求出进入点i的时间fi [ i ],离开点i的时间fo [ i ]。
按dfn序建bit树Tr。
那么,当前Sx的答案就是sumTr( fo [ i ] ) - sumTr( fi [ i ] - 1 )。
注意:不要写RMQ-lca,会爆空间啊。用倍增lca就好……
【代码】
#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))
#define depmin(a,b) ((dep[a]<dep[b])?(a):(b))
using namespace std;
typedef long long ll;
const int N=2000004;
const int kp=18;
int n,m;
int id[N],son[N][26],cnt;
int Tr[N],yg[N];
int fa[N][20],op[N];
int fd,fi[N],fo[N],tim,dep[N];
vector<int> gg[N];
char st[N];
bool cmp(int a,int b) { return (fi[a]<fi[b]); }
void addtrie(int yi)
{
int sl=strlen(st); int now=0;
for(int i=0;i<sl;i++)
{
int hy=st[i]-'a';
if(!son[now][hy]) son[now][hy]=++cnt;
now=son[now][hy];
}
id[yi]=now;
}
void buildfail()
{
int head=1,tail=0;
for(int i=0;i<26;i++)
if(son[0][i]) op[++tail]=son[0][i];
while(head<=tail)
{
int fw=op[head++];
for(int j=0;j<26;j++)
if(son[fw][j])
{
op[++tail]=son[fw][j];
dep[son[fw][j]]=son[dep[fw]][j];
} else son[fw][j]=son[dep[fw]][j];
}
for(int i=1;i<=tail;i++) gg[dep[op[i]]].push_back(op[i]);
}
void dfs(int x)
{
fi[x]=++tim;
for(vector<int>::iterator it=gg[x].begin();it!=gg[x].end();it++)
{
dep[*it]=dep[x]+1;
fa[*it][0]=x;
dfs(*it);
}
fo[x]=tim;
}
void read(int &x)
{
x=0; char ch=getchar(); int f=1;
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
for(; isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
x*=f;
}
int lca(int a,int b)
{
if(dep[a]>dep[b]) swap(a,b);
for(int i=kp;i>=0;i--)
if(dep[fa[b][i]]>=dep[a]) b=fa[b][i];
if(a==b) return a;
for(int i=kp;i>=0;i--)
if(fa[b][i]!=fa[a][i]) a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
void add(int a,int b) { for(int i=a;i<=tim;i+=(i&-i)) Tr[i]+=b; }
int query(int a)
{
int s=0;
for(int i=a;i;i-=i&-i) s+=Tr[i];
return s;
}
int main()
{
read(n); cnt=0;
for(int i=1;i<=n;i++) scanf("%s",st),addtrie(i);
buildfail(); tim=0; dep[0]=1; fa[0][0]=0; dfs(0);
for(int i=1;i<=kp;i++)
for(int j=1;j<=tim;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
read(m);
for(int i=1;i<=m;i++)
{
int ops; read(ops);
if(ops==1)
{
scanf("%s",st);
int sl=strlen(st); int yp=0;
for(int j=0,now=0;j<sl;j++) yg[++yp]=now=son[now][st[j]-'a'];
sort(yg+1,yg+1+yp,cmp);
add(fi[yg[1]],1);
for(int j=2;j<=yp;j++)
{
int lc=lca(yg[j],yg[j-1]);
add(fi[lc],-1); add(fi[yg[j]],1);
}
} else
{
int ask; read(ask);
int ans=query(fo[id[ask]])-query(fi[id[ask]]-1);
printf("%d\n",ans);
}
}
return 0;
}