給定一個大串 \(S\) 以及 \(q\) 次詢問, 每次詢問給定一個串 \(T\) 和區間 \([l,r]\), 求 \(T\) 中有多少本質不一樣的子串不是 \(S[l:r]\) 的子串.php
\(|S|\le 5\times 10^5,q\le 10^5,\sum|T|\le10^6\).c++
普通的碼農字符串題...數組
得到成就: \(40\texttt{min}(2400\texttt{s})\) 內打完 \(3.9\texttt{kB}\) 的代碼(然而並無打完就A...仍是太菜了...)curl
感受考場上若是T1T2沒打滿的話寫個 \(68\) 分沙雕SAM暴力(詢問區間都是原串的 \(17\) 個測試點)就能夠跑路了...分高好寫還不用調...測試
我的的大致思路是: 由於求本質不一樣子串個數是容易的, 因此先補集轉化爲求 \(T\) 的全部本質不一樣的子串中是 \(S[l:r]\) 的串的個數.ui
按照套路咱們維護一個相似掃描線的東西, 用SAM對 \(T\) 的全部下標 \(i\) 求出以 \(i\) 爲右端點且是 \(S[l:r]\) 的子串的最長子串長度. 按照SAM的套路, 這部分的計算就是直接用 \(T\) 在 \(S\) 的SAM上面跑, 若是能夠匹配就匹配, 不能匹配跳到後綴自動機的父親節點上來移動左端點.this
按照上面這樣計算是對整串來講的. 由於還要考慮區間 \([l,r]\) 的事情, 咱們用線段樹合併維護出每一個結點的right集合, 設當前匹配長度爲 \(len\), 那麼只有噹噹前狀態的right集合與 \([l+len-1,r]\) 有交集才說明與 \(S[l:r]\) 匹配. 若是不知足這個條件, 不能按照SAM的普通套路直接跳prt, 而是應該讓 \(len\) 減小 \(1\). 直接跳的話左端點會移動若干個位置, 可能會跳過最優長度. SAM普通套路直接跳prt是由於若是 \(len\) 只減小 \(1\) 而沒有到達prt的長度的話依然沒有改變當前狀態不能匹配的事實.url
然而直接這樣計算鐵定會有重複, 咱們對 \(T\) 的反串建SA求出全部前綴的最長公共後綴長度做爲去重的參考信息. 按照SA求本質不一樣子串個數的套路, 重複的子串必然出如今後綴數組上相鄰的兩個後綴(注意是反串的後綴)上. 假設相鄰的兩個後綴 \(i,j\) 的在原串的對應前綴的最大匹配長度分別是 \(mlen_i,mlen_j\) 且它們的LCP是 \(height\) 的話, 貢獻就是 \(mlen_j-\min(height,mlen_i)\).spa
實際上 \(mlen_j\) 確定不會小於 \(\min(height,mlen_i)\), 由於這部分串是徹底同樣的. 因此直接減就好了.指針
之前用map的寫法被UOJ 64位指針debuff給卡內存了qaq...然而指針線段樹依然在被卡內存...UOJ變97分了QAQ
以前據說今天minusT要更新Subterranean Rose? 完蛋在學校看不了qaq
#include <bits/stdc++.h> const int MAXN=1e6+10; typedef long long intEx; struct Node{ int l; int r; int sum; Node* lch; Node* rch; Node(int,int); void Insert(int); int Query(int,int); }; Node* N[MAXN]; int n; int q; int cnt=1; int root=1; int last=1; int s[MAXN]; int SA[MAXN]; int len[MAXN]; int prt[MAXN]; int buc[MAXN]; int mlen[MAXN]; char buf[MAXN]; int rank[MAXN]; int height[MAXN]; int chd[MAXN][26]; int* x=new int[MAXN]; int* y=new int[MAXN]; void BuildSAM(); void Extend(char); void BuildSA(char*,int); Node* Merge(Node*,Node*); int main(){ freopen("name.in","r",stdin); freopen("name.out","w",stdout); scanf("%s",buf+1); n=strlen(buf+1); for(int i=1;i<=n;i++) Extend(buf[i]); BuildSAM(); scanf("%d",&q); while(q--){ int l,r; scanf("%s",buf+1); scanf("%d%d",&l,&r); int m=strlen(buf+1); int cur=root,curlen=0; for(int i=1;i<=m;i++){ int x=buf[i]-'a'; while(cur!=root&&!chd[cur][x]){ cur=prt[cur]; curlen=len[cur]; } if(chd[cur][x]){ ++curlen; cur=chd[cur][x]; while(cur!=root&&!N[cur]->Query(l+curlen-1,r)){ --curlen; if(curlen<=len[prt[cur]]) cur=prt[cur]; } } mlen[i]=curlen; } std::reverse(buf+1,buf+m+1); BuildSA(buf,m); intEx ans=0; int last=0; for(int i=1;i<=m;i++){ ans+=(m-SA[i]+1)-height[i]; last=std::min(height[i],last); ans-=mlen[m-SA[i]+1]-last; last=mlen[m-SA[i]+1]; } printf("%lld\n",ans); } return 0; } void BuildSAM(){ memset(buc,0,sizeof(int)*(n+1)); for(int i=1;i<=cnt;i++) ++buc[len[i]]; for(int i=1;i<=n;i++) buc[i]+=buc[i-1]; for(int i=cnt;i>=1;i--) s[buc[len[i]]--]=i; for(int i=cnt;i>=1;i--) N[prt[s[i]]]=Merge(N[prt[s[i]]],N[s[i]]); } void Extend(char ch){ int p=last; int x=ch-'a'; int np=++cnt; last=np; len[np]=len[p]+1; N[np]=new Node(1,n); N[np]->Insert(len[np]); while(p&&!chd[p][x]) chd[p][x]=np,p=prt[p]; if(!p) prt[np]=root; else{ int q=chd[p][x]; if(len[q]==len[p]+1) prt[np]=q; else{ int nq=++cnt; memcpy(chd[nq],chd[q],sizeof(chd[q])); N[nq]=new Node(1,n); len[nq]=len[p]+1; prt[nq]=prt[q]; prt[q]=nq; prt[np]=nq; while(p&&chd[p][x]==q) chd[p][x]=nq,p=prt[p]; } } } void Node::Insert(int x){ ++this->sum; if(this->l!=this->r){ int mid=(this->l+this->r)>>1; if(x<=mid){ if(this->lch==NULL) this->lch=new Node(this->l,mid); this->lch->Insert(x); } else{ if(this->rch==NULL) this->rch=new Node(mid+1,this->r); this->rch->Insert(x); } } } int Node::Query(int l,int r){ if(l<=this->l&&this->r<=r) return this->sum; else{ int ans=0; int mid=(this->l+this->r)>>1; if(l<=mid&&this->lch) ans+=this->lch->Query(l,r); if(mid+1<=r&&this->rch) ans+=this->rch->Query(l,r); return ans; } } Node* Merge(Node* a,Node* b){ if(a==NULL) return b; if(b==NULL) return a; Node* N=new Node(a->l,b->r); N->sum=a->sum+b->sum; N->lch=Merge(a->lch,b->lch); N->rch=Merge(a->rch,b->rch); return N; } void BuildSA(char* s,int n){ int m=127; memset(buc+1,0,sizeof(int)*m); for(int i=1;i<=n;i++) ++buc[x[i]=s[i]]; for(int i=1;i<=m;i++) buc[i]+=buc[i-1]; for(int i=n;i>=1;i--) SA[buc[x[i]]--]=i; for(int k=1;k<n;k<<=1){ int p=0; for(int i=n-k+1;i<=n;i++) y[++p]=i; for(int i=1;i<=n;i++) if(SA[i]>k) y[++p]=SA[i]-k; memset(buc+1,0,sizeof(int)*m); for(int i=1;i<=n;i++) ++buc[x[i]]; for(int i=1;i<=m;i++) buc[i]+=buc[i-1]; for(int i=n;i>=1;i--) SA[buc[x[y[i]]]--]=y[i]; std::swap(x,y); x[SA[1]]=1; p=1; for(int i=2;i<=n;i++) x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p; if(p>=n) break; m=p; } for(int i=1;i<=n;i++) rank[SA[i]]=i; int k=0; for(int i=1;i<=n;i++){ if(rank[i]==1) continue; if(k) --k; int j=SA[rank[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k; height[rank[i]]=k; } } Node::Node(int l,int r):l(l),r(r),sum(0),lch(NULL),rch(NULL){}