去年暑假做过,那时候只会打暴搜,被高中奆佬吊打。
后缀自动机万岁,再也不管后缀数组这辣鸡东西了
一、题目
二、解法
本题需要用到一个知识点:广义 Sam \text{Sam} Sam,其实也没有多难,就是在一个新字符串插入时把 L a s t Last Last赋值为 1 1 1,然后都按普通后缀自动机的方法做就行了。
插入完之后把每个串放进去匹配,对每个匹配到的点都暴力跳 p a r e n t parent parent树,打上这个节点所有者的标记,如果所有者超过两个那就把这个节点的标记设为 − 1 -1 −1,表示这个节点没有贡献,最后访问所有节点统计即可。
时间复杂度 O ( n ) O(n) O(n),贴个代码 q w q qwq qwq。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 200005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,len[M],s[M];char c;
struct node
{
int len,fa,ch[26];
node() {memset(ch,0,sizeof ch);len=fa=0;}
};
struct automaton
{
int cnt,last,val[M],ans[M];node a[M];
automaton() {cnt=last=1;}
void add(int c)
{
int p=last,np=last=++cnt;
a[np].len=a[p].len+1;
for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
if(!p) a[np].fa=1;
else
{
int q=a[p].ch[c];
if(a[q].len==a[p].len+1) a[np].fa=q;
else
{
int nq=++cnt;
a[nq]=a[q];a[nq].len=a[p].len+1;
a[q].fa=a[np].fa=nq;
for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
}
}
}
void solve()
{
m=0;
for(int i=1;i<=n;i++)
{
int now=1;
for(int j=1;j<=len[i];j++)
{
now=a[now].ch[s[++m]];
for(int p=now;val[p]!=-1 && val[p]!=i;p=a[p].fa)
if(val[p]==0)
val[p]=i;
else
val[p]=-1;
}
}
for(int i=1;i<=cnt;i++)
if(val[i]>0)
ans[val[i]]+=a[i].len-a[a[i].fa].len;
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
}Sam;
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
Sam.last=1;//广义后缀自动机核心操作
while((c=getchar())!='\n' && c!=EOF)
{
c-= 'a';++len[i];
s[++m]=c;
Sam.add(c);
}
}
Sam.solve();
}