题解 SP8093 JZPGYZ - Sevenk Love Oimaster
后缀树+\(\texttt{bitset}\)
Solution
对于 \(n\) 个文本串,直接在每个串的间隔加入特殊字符后连成一个字符串,并对此建立后缀树。
那么每次查询模式串时,就将模式串放在后缀树中匹配,匹配完成后的节点的子树中属于不同文本串的后缀数就是要求的东西,形象化地讲就是区间数颜色,但是事弱化版(
而区间数颜色,因为 \(n\) 较小,我们可以暴力地用 \(\texttt{bitset}\) 记录属于每个文本串的后缀是否存在,则当前匹配完成后的节点的 \(\texttt{bitset}\) 中 \(1\) 的个数就是答案。用一次 \(\texttt{dfs}\) 就可以求出所有节点的 \(\texttt{bitset}\),直接或运算就行。
时间复杂度 \(\mathcal O(|S|\times \dfrac{n}{\omega})\),其中 \(\omega\) 是计算机位长。
Code
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
const int N=2e5+5,inf=1e8;
int bel[N];
bitset<10005>lf[N];
struct Suffix_Tree{
int n,tot,cnt,now,rem,link[N],s[N],len[N],st[N];
__gnu_pbds::gp_hash_table<int,int>ch[N];
Suffix_Tree():n(0),tot(1),now(1),rem(0){len[0]=inf;}
int node(int p,int l){link[++tot]=1;st[tot]=p;len[tot]=l;return tot;}
void ins(int x){
s[++n]=x;rem++;
for(int lst=1;rem;){
while(rem>len[ch[now][s[n-rem+1]]])
rem-=len[now=ch[now][s[n-rem+1]]];
int &v=ch[now][s[n-rem+1]],c=s[st[v]+rem-1];
if(!v||x==c){
link[lst]=now;lst=now;
if(!v)v=node(n,inf);
else break;
}else{
int u=node(st[v],rem-1);
ch[u][c]=v;ch[u][x]=node(n,inf);
st[v]+=rem-1;len[v]-=rem-1;
link[lst]=v=u;lst=u;
}if(now==1)rem--;else now=link[now];
}
}
void dfs(int u,int dep){
if(dep>=inf)return;
for(auto x:ch[u]){
int v=x.second;
if(dep+len[v]>=inf){
int id=bel[n-(dep+(n-st[v]+1))+1];//计算此后缀属于哪个文本串
lf[v].set(id);
}dfs(v,dep+len[v]);
lf[u]|=lf[v];
}
}
int ask(char*t,int L){
int u=1,dep=0;
for(int i=1;i<=L;){
if(dep>=inf||ch[u].find(t[i]-'a')==ch[u].end())return 0;
u=ch[u][t[i]-'a'],dep+=len[u];
for(int j=st[u];j<st[u]+len[u]&&j<=n&&i<=L;j++,i++)
if(s[j]!=t[i]-'a')return 0;//匹配失败
}return u;
}
}T;
int n,m;
char s[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1,L;i<=n;i++){
scanf("%s",s+1);L=strlen(s+1);
for(int j=1;j<=L;j++)T.ins(s[j]-'a'),bel[T.n]=i,s[j]=0;
T.ins(26+i),bel[T.n]=i;
}T.dfs(1,0);
for(int i=1,L;i<=m;i++){
scanf("%s",s+1);L=strlen(s+1);
int u=T.ask(s,L);fill(s+1,s+L+1,0);
printf("%d\n",lf[u].count());
}
return 0;
}
后缀树大法好哇

浙公网安备 33010602011771号