【文文殿下】[BZOJ3277] 串
阿新 • • 發佈:2018-11-26
desc 多少 多次 lld 一次 clu sort 問題 前綴
當這個字符串跑到某一個節點時,我們把從這個節點,沿著parent樹直到根部全部增加。因為一個串在匹配的時候,有可能繞過了他的parent直接匹配到該節點。
然後,對於每一個串,再跑一邊。當他的右端點匹配到某個狀態以後,立即進行“清算”,把它能夠匹配的後綴全部統計。即:沿著Parent樹全部加入答案。
但是這樣是n^2的,我們預處理前綴和可以做到O(n)
Description
字符串是oi界常考的問題。現在給定你n個字符串,詢問每個字符串有多少子串(不包括空串)是所有n個字符串中
至少k個字符串的子串(註意包括本身)
Input
第一行兩個整數n,k。
接下來n行每行一個字符串。
n,k,l<=100000
Output
輸出一行n個整數,第i個整數表示第i個字符串的答案。
Sample Input
3 1
abc
a
ab
Sample Output
6 1 3
題解
多個字符串,考慮建廣義後綴自動機。
對於每個節點,記錄它在每個字符串出現的次數。
但是為了防止重復記錄(一個字符串在他的SAM上可能多次匹配到同一個點),我們對每個節點記錄一個“上一次統計到的是哪個字符串”,這樣子進行有序增加。
然後,對於每一個串,再跑一邊。當他的右端點匹配到某個狀態以後,立即進行“清算”,把它能夠匹配的後綴全部統計。即:沿著Parent樹全部加入答案。
但是這樣是n^2的,我們預處理前綴和可以做到O(n)
#include<cstdio> #include<cstring> #include<algorithm> #include<string> typedef long long ll; const int maxn = 2e5+20; ll ans=0; int tmp[maxn]; int par[maxn],mx[maxn],tr[maxn][26]; char A[maxn>>1]; int f[maxn]; int cnt = 1,last = 1,Right[maxn]; int c[maxn],id[maxn]; int pre[maxn]; std::string S[maxn]; int n,k; void extend(int x) { int p = last; if(tr[p][x]&&mx[tr[p][x]]==mx[p]+1) {last=tr[p][x];return;} int np=++cnt; mx[np]=mx[p]+1; while(p&&!tr[p][x]) tr[p][x]=np,p=par[p]; if(!p) par[np]=1; else { int q = tr[p][x]; if(mx[q]==mx[p]+1) par[np]=q; else { int nq = ++cnt; mx[nq]=mx[p]+1; memcpy(tr[nq],tr[q],sizeof tr[q]); par[nq]=par[q]; par[q]=par[np]=nq; while(p&&tr[p][x]==q) tr[p][x]=nq,p=par[p]; } } last = np; return; } inline void topsort() { for(int i = 1;i<=cnt;++i) ++c[mx[i]]; for(int i = 1;i<=cnt;++i) c[i]+=c[i-1]; for(int i = 1;i<=cnt;++i) id[c[mx[i]]--]=i; return; } int main() { scanf("%d%d",&n,&k); for(int i = 1;i<=n;++i) { scanf("%s",A); S[i]=std::string(A); int len = S[i].length(); last = 1; for(int j = 0;j<len;++j) extend(S[i][j]-'a'); } for(int i = 1;i<=n;++i) { int len = S[i].length(); int cur = 1; for(int j = 0;j<len;++j) { int c = S[i][j]-'a'; while(cur&&!tr[cur][c]) cur=par[cur]; if(!cur) cur=1; else cur = tr[cur][c]; int tmp = cur; if(tmp&&pre[tmp]!=i) pre[tmp]=i,++Right[tmp],tmp=par[tmp]; } } topsort(); //Right[1]=0; for(int i = 1;i<=cnt;++i) f[id[i]]=f[par[id[i]]]+(Right[id[i]]>=k?mx[id[i]]-mx[par[id[i]]]:0); for(int i = 1;i<=n;++i) { int len = S[i].length(); int cur=1,L=0; ll ans = 0; for(int j = 0;j<len;++j) { int c = S[i][j]-'a'; while(cur&&!tr[cur][c]) cur=par[cur]; if(!cur) cur=1,L=0; else L=std::min(L,mx[cur])+1,cur = tr[cur][c]; if(L==mx[cur]||Right[cur]<k) ans+=f[cur]; else { ans+=f[cur]; ans-=mx[cur]; ans+=L; } } printf("%lld ",ans); } return 0; }
【文文殿下】[BZOJ3277] 串