【[USACO17DEC]Standing Out from the Herd】

题目

不会广义\(SAM\)

但信仰插入特殊字符就可以搞定一切了

我们先把所有的串搞在一起建出一个\(SAM\),记得在中间插入特殊字符

对于\(parent\)树上的一个节点,只有当其\(endpos\)集合中的所有元素都来自于同一个串的时候我们才对它进行统计

所以我们需要维护一个\(parent\)树上子树最大值和最小值,这样我们就能很快的判断这个\(endpos\)是否合法了

但是非常棘手的一类情况是我们插进去的特殊字符,显然\(SAM\)里有一些奇奇怪怪的子串中含有特殊字符

那也好办,我们存好每个串的起始位置只要那些没有跨越特殊字符的就好了

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 200005
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
struct E{int v,nxt;} e[maxn];
char S[maxn];
int m,n,lst=1,cnt=1,num;
int len[maxn],fa[maxn],son[maxn][27],mx[maxn],ml[maxn];
int f[maxn],head[maxn],d[maxn],Ans[maxn],li[maxn];
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
void dfs(int x) {
	for(re int i=head[x];i;i=e[i].nxt) 
		dfs(e[i].v),mx[x]=max(mx[e[i].v],mx[x]),ml[x]=min(ml[e[i].v],ml[x]),d[x]|=d[e[i].v];
}
inline void ins(int c,int o)
{
	int f=lst,p=++cnt; lst=p;
	len[p]=len[f]+1,mx[p]=ml[p]=o;
	if(c==26) d[p]=1;
	while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
	if(!f) {fa[p]=1;return;}
	int x=son[f][c];
	if(len[f]+1==len[x]) {fa[p]=x;return;}
	int y=++cnt;
	len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
	for(re int i=0;i<27;i++) son[y][i]=son[x][i];
	while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
int main()
{
	memset(ml,20,sizeof(ml));
	scanf("%d",&m);
	for(re int i=1;i<=m;i++)
	{
		scanf("%s",S+1);
		int len=strlen(S+1);li[i]=n+1;
		for(re int j=1;j<=len;j++) ins(S[j]-'a',++n),f[n]=i;
		ins(26,++n);
	}
	for(re int i=2;i<=cnt;i++) add(fa[i],i);
	dfs(1);
	for(re int i=2;i<=cnt;i++)
	{
		if(d[i]) continue;
		if(f[mx[i]]!=f[ml[i]]) continue;
		int minlen=len[fa[i]]+1;
		if(f[mx[i]-minlen+1]!=f[mx[i]]) continue;
		if(f[mx[i]-len[i]+1]==f[mx[i]]) Ans[f[mx[i]]]+=len[i]-len[fa[i]];
			else Ans[f[mx[i]]]+=mx[i]-li[f[mx[i]]]-minlen+2;
	}
	for(re int i=1;i<=m;i++) printf("%d\n",Ans[i]);
	return 0;
}
posted @ 2019-01-10 12:43  asuldb  阅读(198)  评论(0编辑  收藏  举报