喵星球上的点名——记一个用广义SAM根号维护多串的技巧
喵星球的上的点名
给定一个字符串集合S,每次给定T询问S中有多少个字符串中包含T,最后询问S中的每一个字符串包含了多少次给定的T。
思路
考虑将这S个字符串建立广义SAM,那么我们每次将T放到广义SAM中去匹配,最后匹配到的节点的parent子树中来自不同串的结束位置数就是第一问的答案。
同样我们每次匹配完之后在节点处打上标记,第二问S中每一个字符串的答案就是每一个结束位置能够一直往上跳链跳到的不同的节点的标记之和。
考虑如何实现上述过程,在广义SAM中,假设一个节点在SAM中的有效节点数量为\(S_i\),那么可以证明\(\sum_{i=1}^{n}S_i\leq n\sqrt{2n}\),于是对于第一问我们只需要对于每一个结束位置暴力标记parent树上的节点,第二问每一个结束位置去暴力统计parent树上的节点即可。
/*=======================================
* Author : ylsoi
* Time : 2019.7.2
* Problem : luogu2336
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("luogu2336.in","r",stdin);
freopen("luogu2336.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
string proc(){
ifstream f("/proc/self/status");
return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}
const int maxn=2e5+10;
int n,m,name[maxn],len1[maxn],len2[maxn];
unordered_map<int,int>ch[maxn<<1];
int fa[maxn<<1],len[maxn<<1],cnt=1,last=1;
int vis[maxn<<1],sz[maxn<<1],fg[maxn<<1];
void insert(int x){
int p=last,np=last=++cnt;
len[np]=len[p]+1;
while(p && !ch[p][x])ch[p][x]=np,p=fa[p];
if(!p)fa[np]=1;
else{
int q=ch[p][x];
if(len[q]==len[p]+1)fa[np]=q;
else{
int nq=++cnt;
ch[nq]=ch[q],len[nq]=len[p]+1,fa[nq]=fa[q];
fa[q]=fa[np]=nq;
while(p && ch[p][x]==q)ch[p][x]=nq,p=fa[p];
}
}
}
void update(int p,int id){
for(;p && vis[p]!=id;p=fa[p])
vis[p]=id,++sz[p];
}
int query(int p,int id){
int ret=0;
for(;p && vis[p]!=id;p=fa[p])
vis[p]=id,ret+=fg[p];
return ret;
}
int main(){
File();
read(n),read(m);
int now=0;
REP(i,1,n){
read(len1[i]),last=1;
REP(j,1,len1[i])read(name[++now]),insert(name[now]);
read(len2[i]),last=1;
REP(j,1,len2[i])read(name[++now]),insert(name[now]);
}
now=0;
REP(i,1,n){
int o=1;
REP(j,1,len1[i])update(o=ch[o][name[++now]],i);
o=1;
REP(j,1,len2[i])update(o=ch[o][name[++now]],i);
}
REP(i,1,m){
int len0,x,o=1;
read(len0);
REP(j,1,len0)read(x),o=ch[o][x];
printf("%d\n",sz[o]);
++fg[o];
}
memset(vis,0,sizeof(vis));
now=0;
REP(i,1,n){
int o=1,ans=0;
REP(j,1,len1[i])ans+=query(o=ch[o][name[++now]],i);
o=1;
REP(j,1,len2[i])ans+=query(o=ch[o][name[++now]],i);
printf("%d ",ans);
}
return 0;
}