转载注明来源:https://www.cnblogs.com/syc233/p/13784426.html
广义SAM+区间数颜色。
几篇写广义SAM的题解似乎都没写对啊/fad,都是插入新串时直接将 \(las\) 设为 \(1\) 的,在这篇博客中提到这样建出来的广义SAM是有问题的。
所以这里再写一篇题解来讲一下如何正确使用广义SAM解决这道题(当然也不能保证完全正确,毕竟我也才学广义SAM)。
对所有模板串建立一个广义SAM,每个结点记录它存储的串的信息的编号。因为在广义SAM中,一个结点可能保存了多个模式串的状态,所以需要每个点开一个vector来记录,而不是单纯的使用一个数组。
查询时,找到查询串在SAM上的对应结点,那么现在只需要知道这个结点代表的字符串在哪些模板串中出现过。
由于parent树的性质,若一个结点代表的字符串在某个模板串中出现,那么它的祖先代表的字符串也会在这个模板串中出现。
于是问题就变成了:查询parent树上一个子树内结点存储的模板串种类数,即子树内颜色数,转换成dfs序即为区间数颜色问题。
区间数颜色问题可以参考 P1972 [SDOI2009]HH的项链 。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=1e4+5,maxq=6e4+5,maxnode=2e5+5;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
struct edge
{
int u,v,next;
edge(int u,int v,int next):u(u),v(v),next(next){}
edge(){}
}e[maxnode];
int head[maxnode],ecnt;
inline void add(int u,int v)
{
e[ecnt]=edge(u,v,head[u]);
head[u]=ecnt++;
}
int n,m;
struct State
{
int len,fa;
int ch[30];
}S[maxnode];
int tot=1;
inline int insert(int c,int las)
{
int p=las;
if(S[p].ch[c])
{
int q=S[p].ch[c];
if(S[q].len==S[p].len+1) return q;
else
{
int nq=++tot;S[nq]=S[q];
S[nq].len=S[p].len+1;
S[q].fa=nq;
for(;p&&S[p].ch[c]==q;p=S[p].fa) S[p].ch[c]=nq;
return nq;
}
}
int np=++tot;
S[np].len=S[p].len+1;
for(;p&&!S[p].ch[c];p=S[p].fa) S[p].ch[c]=np;
if(!p) S[np].fa=1;
else
{
int q=S[p].ch[c];
if(S[q].len==S[p].len+1) S[np].fa=q;
else
{
int nq=++tot;S[nq]=S[q];
S[nq].len=S[p].len+1;
S[q].fa=S[np].fa=nq;
for(;p&&S[p].ch[c]==q;p=S[p].fa) S[p].ch[c]=nq;
}
}
return np;
}
struct BIT
{
int sum[maxnode];
inline int lowbit(int x){return x&-x;}
inline void add(int x,int d)
{
for(int i=x;i<=tot;i+=lowbit(i))
sum[i]+=d;
}
inline int query(int x)
{
int res=0;
for(int i=x;i>=1;i-=lowbit(i))
res+=sum[i];
return res;
}
}bit;
struct ques
{
int l,r,id;
ques(int l,int r,int id):l(l),r(r),id(id){}
ques(){}
inline bool operator < (const ques &T)const
{
return r<T.r;
}
}q[maxq];
int qcnt;
char s[maxnode<<1];
int ans[maxq];
vector<int> color[maxnode];
int pre[maxn];
int dfn[maxnode],idx[maxnode],siz[maxnode],dfs_cnt;
void dfs(int u)
{
dfn[u]=++dfs_cnt;
idx[dfs_cnt]=u;
siz[u]=1;
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].v;
dfs(v);
siz[u]+=siz[v];
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("SP8093.in","r",stdin);
#endif
read(n),read(m);
for(int i=1;i<=n;++i)
{
scanf(" %s\n",s+1);
int len=strlen(s+1);
for(int j=1,las=1;j<=len;++j)
{
las=insert(s[j]-'a',las);
color[las].push_back(i);
}
}
memset(head,-1,sizeof(head));
for(int i=2;i<=tot;++i)
add(S[i].fa,i);
dfs(1);
for(int i=1;i<=m;++i)
{
scanf(" %s\n",s+1);
int u=1,len=strlen(s+1);
for(int j=1;j<=len&&u;++j)
u=S[u].ch[s[j]-'a'];
if(u) q[++qcnt]=ques(dfn[u],dfn[u]+siz[u]-1,i);// 询问离线
}
sort(q+1,q+qcnt+1);
for(int i=1,p=1;i<=qcnt;++i)
{
int u=idx[p];
while(p<=q[i].r)
{
for(auto c:color[u])
{
if(pre[c]) bit.add(pre[c],-1);
bit.add(pre[c]=p,1);
}
u=idx[++p];
}
ans[q[i].id]=bit.query(q[i].r)-bit.query(q[i].l-1);
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}