Luogu4081 USACO17DEC Standing Out from the Herd(广义后缀自动机)

  建出广义SAM,通过parent树对每个节点求出其是否仅被一个子串包含及被哪个包含。

  写了无数个sam板子题一点意思都没啊

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1000000010
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,son[N][26],fail[N],len[N],f[N],q[N],tmp[N],cnt=1,last;
ll ans[N];
char s[N];
int ins(int c,int p,int i)
{
	int u;
    if (son[p][c])
    {
        int q=son[p][c];
        if (len[p]+1==len[q]) u=q;
        else
        {
            int y=++cnt;
            len[y]=len[p]+1;
            memcpy(son[y],son[q],sizeof(son[q]));
            fail[y]=fail[q],fail[q]=y;
            while (son[p][c]==q) son[p][c]=y,p=fail[p];
            u=y;
        }
    }
    else
    {
        int x=++cnt;len[x]=len[p]+1;
        while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
        if (!p) fail[x]=1;
        else
        {
            int q=son[p][c];
            if (len[p]+1==len[q]) fail[x]=q;
            else
            {
                int y=++cnt;
                len[y]=len[p]+1;
                memcpy(son[y],son[q],sizeof(son[q]));
                fail[y]=fail[q],fail[x]=fail[q]=y;
                while (son[p][c]==q) son[p][c]=y,p=fail[p];
            }
        }
        u=x;
    }
    if (f[u]==-1) f[u]=i;else if (f[u]!=i) f[u]=0;
    return u;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	n=read();memset(f,255,sizeof(f));
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		m=strlen(s+1);last=1;
		for (int j=1;j<=m;j++) last=ins(s[j]-'a',last,i);
	}
	for (int i=1;i<=cnt;i++) tmp[len[i]]++;
	for (int i=1;i<=cnt;i++) tmp[i]+=tmp[i-1];
	for (int i=1;i<=cnt;i++) q[tmp[len[i]]--]=i;
	for (int i=cnt;i>=1;i--)
	{
		int x=q[i];
		if (f[x]>0) ans[f[x]]+=len[x]-len[fail[x]];
		if (f[fail[x]]==-1) f[fail[x]]=f[x];
		else if (f[fail[x]]!=f[x]) f[fail[x]]=0;
	}
	for (int i=1;i<=n;i++) printf("%lld\n",ans[i]);
	return 0;
	//NOTICE LONG LONG!!!!!
}

  

转载于:https://www.cnblogs.com/Gloid/p/10832153.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值