bzoj 3277

时间:2021-10-10 18:16:39

十分之恶心的后缀自动机

(其实是水题,但是我太弱了...)

首先,有一个预备知识:bzoj 2780https://blog.csdn.net/lleozhang/article/details/89365183

现在我们假定你会了这道题

然后我们来讨论这个问题:

套路是一样的:仍然建起广义后缀自动机,然后搞出parent树

首先我们要想一个问题:如何确定某一个子串在这些串中出现的次数呢?

回顾一下这条性质:一个子串一定是一个前缀的后缀

所以我们在建起的后缀自动机上跑每一个串,在跑到每一个节点的时候暴力跳pre指针,如果能跳到某一个节点就证明跳到的节点所对应的子串是现在跑到的节点的子串,那么我们累计一下每个节点被跳到的次数,也就是他对应的子串在不同串中出现的次数了。

对于每一个出现次数>=k的节点,我们记它的val为它的len-它pre的len,然后在parent树上累计从根节点到该节点路径上的权值和,然后再跑一遍串,将经过的节点的权值和累加即为对应串的答案。

很显然你并没有看懂

所以我们做出解释:

首先,出现次数大于k的节点,这个节点相对他pre指针指向节点所多出的子串个数为len之差,那么这就是权值的初始值

紧接着,我们能够发现:在parent树上如果一个子节点是合法的,那么父节点一定是合法的,因为父节点是子节点的后缀,所以我们对每个子节点去累计它祖宗节点的总贡献即可

最后,我们在后缀自动机上跑串,累加跑到点的贡献即可

(当然,本题在统计子串出现次数的时候并没有使用离线树状数组,这是因为...会T!!!)

(upd:离线树状数组并不会T,只是因为...我把数组开大了,直接导致bzoj误将mle判成tle,所以在下面也贴上了树状数组的代码)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#define ll long long
using namespace std;
struct SAM
{
int tranc[27];
int endpos;
int len;
int num;
int pre;
}s[200005];
struct Edge
{
int next;
int to;
}edge[200005];
char ch[100005];
int head[200005];
int edt[200005];
int ret[200005];
int ilen[200005];
int last[200005];
int sch[100005];
int huge[200005];
int tot;
int dep;
int cnt=1;
int las,siz;
int n,k;
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void add(int l,int r)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
head[l]=cnt++;
} void ins(int c,int typ)
{
int nwp=++siz;
s[nwp].endpos=typ;
s[nwp].len=s[las].len+1;
int lsp;
for(lsp=las;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)s[lsp].tranc[c]=nwp;
if(!lsp)
{
s[nwp].pre=1;
}else
{
int lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+1)
{
s[nwp].pre=lsq;
}else
{
int nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+1;
s[nwq].endpos=0;
s[lsq].pre=s[nwp].pre=nwq;
while(s[lsp].tranc[c]==lsq)s[lsp].tranc[c]=nwq,lsp=s[lsp].pre;
}
}
las=nwp;
}
void buildtree()
{
init();
for(int i=2;i<=siz;i++)add(s[i].pre,i);
}
void redfs(int x)
{
ret[x]+=ret[s[x].pre];
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
redfs(to);
}
}
int main()
{
scanf("%d%d",&n,&k);
las=++siz;
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
ilen[i]=strlen(ch+1);
for(int j=1;j<=ilen[i];j++)ins(ch[j]-'a'+1,i),sch[++tot]=ch[j]-'a'+1;
las=1;
}
buildtree();
int lass=0;
for(int i=1;i<=n;i++)
{
int las=1;
for(int j=1;j<=ilen[i];j++)
{
int temp=s[las].tranc[sch[j+lass]];
las=temp;
while(temp!=1&&last[temp]!=i)
{
huge[temp]++;
last[temp]=i;
temp=s[temp].pre;
}
}
lass+=ilen[i];
}
for(int i=1;i<=siz;i++)if(huge[i]>=k)ret[i]=s[i].len-s[s[i].pre].len;
redfs(1);
lass=0;
for(int i=1;i<=n;i++)
{
int las=1;
ll rans=0;
for(int j=1;j<=ilen[i];j++)
{
las=s[las].tranc[sch[j+lass]];
rans+=1ll*ret[las];
}
printf("%lld ",rans);
lass+=ilen[i];
}
printf("\n");
return 0;
}
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct SAM
{
int tranc[27];
int endpos;
int len;
int num;
int pre;
}s[200005];
struct Edge
{
int next;
int to;
}edge[200005];
char ch[400005];
int sum[400005];
int head[400005];
int inr[400005];
int our[400005];
int last[400005];
int f[400005];
int edt[400005];
int ret[400005];
int inrt[400005];
int ilen[400005];
int sch[400005];
int tot;
int dep;
int cnt=1;
int las,siz;
int n,k;
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int y)
{
while(x<=dep)
{
sum[x]+=y;
x+=lowbit(x);
}
}
int get_sum(int x)
{
int ans=0;
while(x)
{
ans+=sum[x];
x-=lowbit(x);
}
return ans;
}
void add(int l,int r)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
head[l]=cnt++;
}
void ins(int c,int typ)
{
int nwp=++siz;
s[nwp].endpos=typ;
s[nwp].len=s[las].len+1;
int lsp;
for(lsp=las;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)s[lsp].tranc[c]=nwp;
if(!lsp)
{
s[nwp].pre=1;
}else
{
int lsq=s[lsp].tranc[c];
if(s[lsq].len==s[lsp].len+1)
{
s[nwp].pre=lsq;
}else
{
int nwq=++siz;
s[nwq]=s[lsq];
s[nwq].len=s[lsp].len+1;
s[nwq].endpos=0;
s[lsq].pre=s[nwp].pre=nwq;
while(s[lsp].tranc[c]==lsq)s[lsp].tranc[c]=nwq,lsp=s[lsp].pre;
}
}
las=nwp;
}
void buildtree()
{
init();
for(int i=2;i<=siz;i++)add(s[i].pre,i);
}
void dfs(int x)
{
inr[x]=++dep;
f[dep]=x;
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
dfs(to);
}
our[x]=++dep;
edt[dep]=x;
}
/*void bfs()
{
queue <int> M;
M.push(1);
while(!M.empty())
{
int u=M.front();
M.pop();
for(int i=1;i<=26;i++)
{
int to=s[u].tranc[i];
if(to)
{
inrt[to]--;
ret[to]+=ret[u];
if(!inrt[to])M.push(to);
}
}
}
}*/
void redfs(int x)
{
ret[x]+=ret[s[x].pre];
for(int i=head[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
redfs(to);
}
}
int main()
{
scanf("%d%d",&n,&k);
las=++siz;
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
ilen[i]=strlen(ch+1);
for(int j=1;j<=ilen[i];j++)ins(ch[j]-'a'+1,i),sch[++tot]=ch[j]-'a'+1;
las=1;
}
buildtree();
dfs(1);
for(int i=1;i<=dep;i++)
{
update(i,1);
if(last[s[f[i]].endpos])update(last[s[f[i]].endpos],-1);
last[s[f[i]].endpos]=i;
if(edt[i]&&get_sum(i)-get_sum(inr[edt[i]]-1)>=k+1)ret[edt[i]]+=s[edt[i]].len-s[s[edt[i]].pre].len;
else ret[edt[i]]=0;
}
redfs(1);
int lass=0;
for(int i=1;i<=n;i++)
{
int las=1;
int rans=0;
for(int j=1;j<=ilen[i];j++)
{
rans+=ret[las];
las=s[las].tranc[sch[j+lass]];
}
rans+=ret[las];
lass+=ilen[i];
printf("%d ",rans);
}
printf("\n");
return 0;
}