题目大意
一篇文章由
n
个单词组成。询问每个单词在文章中作为某单词(包括自己)的子串总共出现了多少次(如果在某单词中多次出现,算多次)?
单词都由小写字母组成。
令单词总长为
题目分析
在
Trie
上建一个
SAM
,然后直接计算即可。
当然如果使用
SAM
构造广义后缀树的话代码会简单很多,但是我目前还不会。学习了之后会进行更新。
令
T
为
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=1005000;
const int S=N<<1;
const int C=26;
int que[S],head,tail;
struct Node
{
int prt,len,size,deg;
int next[C];
}sam[S];
int tot;
int ins(int x,int c,int s)
{
int p=x,np=++tot;
for (sam[np].size=s,sam[np].len=sam[p].len+1;p&&!sam[p].next[c];p=sam[p].prt)
sam[p].next[c]=np;
if (!p)
sam[np].prt=1;
else
{
int q=sam[p].next[c];
if (sam[q].len==sam[p].len+1)
sam[np].prt=q;
else
{
int nq=++tot;
sam[nq]=sam[q],sam[nq].size=0;
sam[nq].len=sam[p].len+1;
sam[nq].prt=sam[q].prt;
sam[q].prt=sam[np].prt=nq;
for (;p&&sam[p].next[c]==q;p=sam[p].prt)
sam[p].next[c]=nq;
}
}
return np;
}
void calc()
{
for (int i=2;i<=tot;i++) sam[sam[i].prt].deg++;
for (int i=2;i<=tot;i++)
if (!sam[i].deg) que[++tail]=i;
int x;
while (head!=tail)
{
x=que[++head];
sam[sam[x].prt].size+=sam[x].size;
if (!--sam[sam[x].prt].deg&&x!=1)
que[++tail]=sam[x].prt;
}
}
struct Trie
{
int next[N][C],suf[N],size[N];
int tot;
int newnode()
{
for (int c=0;c<C;c++) next[tot][c]=0;
suf[tot]=size[tot]=0;
return tot++;
}
int insert(int rt,int c)
{
if (!next[rt][c]) next[rt][c]=newnode();
rt=next[rt][c],size[rt]++;
return rt;
}
void build(int rt)
{
for (int c=0,x;c<C;c++)
if (x=next[rt][c])
suf[x]=ins(suf[rt],c,size[x]),build(x);
}
}t;
char s[N];
int n,l;
int main()
{
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
scanf("%d",&n);
l=0;
t.tot++;
for (int i=1;i<=n;i++)
{
char ch=getchar();
int rt=0;
while (!isalpha(ch)) ch=getchar();
while (isalpha(ch)) s[l]=ch,rt=t.insert(rt,s[l++]-'a'),ch=getchar();
s[l++]='$';
}
t.suf[0]=tot=1;
t.build(0);
calc();
for (int i=1,cur=0,p;i<=n;i++)
{
p=1;
while (s[cur]!='$') p=sam[p].next[s[cur++]-'a'];
cur++;
printf("%d\n",sam[p].size);
}
fclose(stdin);
fclose(stdout);
}