<span>题解「SPOJ8093 JZPGYZ - Sevenk Love Oimaster」</span>

转载注明来源: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;
}

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-27 10:52
点赞 评论 收藏
分享
10-11 17:45
门头沟学院 Java
走吗:别怕 我以前也是这么认为 虽然一面就挂 但是颇有收获!
点赞 评论 收藏
分享
努力学习的小绵羊:我反倒觉得这种挺好的,给不到我想要的就别浪费大家时间了
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务