BZOJ.5417.[NOI2018]你的名字(字尾自動機 線段樹合併)
阿新 • • 發佈:2018-11-09
考慮\(l=1,r=|S|\)的情況:
對\(S\)串建SAM,\(T\)在上面匹配,可以得到每個位置\(i\)的字尾的最長匹配長度\(mx[i]\)。
因為要去重,對\(T\)也建SAM,計算上面所有節點的答案。記\(pos[i]\)表示\(i\)節點第一次出現的下標(同一節點代表的串出現的位置集合相同,所以隨便記一個即可)。
則節點\(i\)的答案為:\(\max(0,\ len[i]-\max(len[fa[i]],\ mx[pos[i]]))\)。
考慮\(l,r\)任意的情況:
要判斷\(T\)能否在\(S[l,r]\)上匹配,也就是匹配的時候只能走在\(S[l,r]\)
線段樹合併,自底向上合併right集合,就可以得到SAM上每個節點出現過的位置(right)。
如果已匹配長度為\(now\),那麼區間查的時候要查\([l+now,r]\)啊(跳\(fa\)時要同時改\(now\))(要不還是68分)。
而且\(now\)應該是每次減一,而不是令\(p\)直接跳\(fa\)(但還是有96分)。
好像只要還記得SAM的套路就沒那麼難?(反正我已經忘了)
明年還出SAM就好了。
/* LOJ:10770ms 337788K 洛谷:16362ms 348.41MB BZOJ:408052kb 28352ms */ #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() typedef long long LL; const int N=1e6+5; int mx[N],root[N]; char s[N]; struct Segment_Tree { #define ls son[x][0] #define rs son[x][1] #define lson ls,l,m #define rson rs,m+1,r #define S N*20 int tot,son[S][2]; #undef S void Insert(int &x,int l,int r,int p) { /*if(!x)*/ x=++tot;// if(l==r) return; int m=l+r>>1; p<=m ? Insert(lson,p) : Insert(rson,p); } int Merge(int x,int y) { if(!x||!y) return x|y; int now=++tot; son[now][0]=Merge(ls,son[y][0]), son[now][1]=Merge(rs,son[y][1]); return now; } bool Query(int x,int l,int r,int L,int R) { if(!x) return 0; if(L<=l && r<=R) return 1; int m=l+r>>1; if(L<=m) if(m<R) return Query(lson,L,R)||Query(rson,L,R); else return Query(lson,L,R); return Query(rson,L,R); } #undef ls #undef rs }Tr; struct Suffix_Automaton { int n,tot,las,fa[N],son[N][26],len[N],tm[N],A[N],pos[N]; void Insert(int c,int id) { int np=++tot,p=las; len[las=np]=len[p]+1, pos[np]=id; for(;p&&!son[p][c]; p=fa[p]) son[p][c]=np; if(!p) fa[np]=1; else { int q=son[p][c]; if(len[q]==len[p]+1) fa[np]=q; else { int nq=++tot; len[nq]=len[p]+1, pos[nq]=pos[q]; memcpy(son[nq],son[q],sizeof son[q]); fa[nq]=fa[q], fa[q]=fa[np]=nq; for(; son[p][c]==q; p=fa[p]) son[p][c]=nq; } } } void Build_S(char *s,int l) { memset(son,0,(tot+2)*sizeof son[0]); tot=las=1, n=l; for(int i=1; i<=n; ++i) Insert(s[i]-'a',i),Tr.Insert(root[las],1,n,i); for(int i=1; i<=tot; ++i) ++tm[len[i]]; for(int i=1; i<=n; ++i) tm[i]+=tm[i-1]; for(int i=1; i<=tot; ++i) A[tm[len[i]]--]=i; for(int i=tot,x=A[i]; i>1; x=A[--i]) root[fa[x]]=Tr.Merge(root[x],root[fa[x]]); } void Build_T(char *s,int l) { memset(son,0,(tot+2)*sizeof son[0]); tot=las=1, n=l; for(int i=1; i<=l; ++i) Insert(s[i]-'a',i); } }S,T; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } void Solve(int ls) { scanf("%s",s+1); int l=read(),r=read(),n=strlen(s+1); T.Build_T(s,n); for(int i=1,now=0,p=1,c; i<=n; mx[i++]=now) { c=s[i]-'a'; while(1) { if(!Tr.Query(root[S.son[p][c]],1,ls,l+now,r)) { if(!now) break;//匹配長度為0了要再匹配一次 --now; if(now==S.len[S.fa[p]]) p=S.fa[p]; } else {++now, p=S.son[p][c]; break;} } // if(Tr.Query(root[S.son[p][c]],1,ls,l+now,r)) p=S.son[p][c], ++now; // else // { // for(; p&&!Tr.Query(root[S.son[p][c]],1,ls,l+now,r); p=S.fa[p],now=S.len[p]); // if(!p) p=1, now=0; // else now=S.len[p]+1, p=S.son[p][c];//這樣寫96分 還只錯了一個詢問 // } } LL ans=0; for(int i=2; i<=T.tot; ++i) ans+=std::max(0,T.len[i]-std::max(T.len[T.fa[i]],mx[T.pos[i]])); printf("%lld\n",ans); } int main() { // freopen("name.in","r",stdin); // freopen("name.out","w",stdout); scanf("%s",s+1); int ls=strlen(s+1); S.Build_S(s,ls); for(int Q=read(); Q--; Solve(ls)); return 0; }
68分程式碼:
/* 對S串建SAM,T在上面匹配,可以得到每個位置i的字尾的最長匹配長度mx[i]。 因為要去重,對T也建SAM,計算上面所有節點的答案。記pos[i]表示i節點第一次出現的下標(同一節點代表的串出現的位置集合相同)。 則節點i的答案為:max(0, len[i]-max(len[fa[i]], mx[pos[i]]))。 */ #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() typedef long long LL; const int N=1e6+5; int mx[N]; char s[N]; struct Suffix_Automaton { int n,tot,las,fa[N],son[N][26],len[N],pos[N]; void Insert(int c,int id) { int np=++tot,p=las; len[las=np]=len[p]+1, pos[np]=id; for(;p&&!son[p][c]; p=fa[p]) son[p][c]=np; if(!p) fa[np]=1; else { int q=son[p][c]; if(len[q]==len[p]+1) fa[np]=q; else { int nq=++tot; len[nq]=len[p]+1, pos[nq]=pos[q]; memcpy(son[nq],son[q],sizeof son[q]); fa[nq]=fa[q], fa[q]=fa[np]=nq; for(; son[p][c]==q; p=fa[p]) son[p][c]=nq; } } } void Build(char *s,int l) { memset(son,0,(tot+2)*sizeof son[0]); tot=las=1, n=l; for(int i=1; i<=n; ++i) Insert(s[i]-'a',i); } }S,T; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } void Solve() { int n=strlen(s+1); for(int i=1,now=0,p=1,c; i<=n; mx[i++]=now) { if(S.son[p][c=s[i]-'a']) p=S.son[p][c], ++now; else { for(; p&&!S.son[p][c]; p=S.fa[p]); if(!p) p=1, now=0; else now=S.len[p]+1, p=S.son[p][c]; } } LL ans=0; for(int i=2; i<=T.tot; ++i) ans+=std::max(0,T.len[i]-std::max(T.len[T.fa[i]],mx[T.pos[i]])); printf("%lld\n",ans); } int main() { freopen("name.in","r",stdin); freopen("name.out","w",stdout); scanf("%s",s+1); S.Build(s,strlen(s+1)); int Q=read(); for(int i=1; i<=Q; ++i) { scanf("%s",s+1); int l=read(),r=read(); T.Build(s,strlen(s+1)), Solve(); } return 0; }