十分之噁心的後綴自動機
(其實是水題,但是我太弱了...)
首先,有一個預備知識: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;
}