我有種自己根本沒學過SAM的感覺……最后還是抄了老半天的題解……
首先,對$S$和每一次的$T$都建一個SAM
先考慮一下$l=1,r=\left| S \right|$的情況
設$lim_i$表示字符串$T[1..i]$能在$S$中匹配到的最長后綴(即$T[i-lim_i+1,i]$是$S$的子串且$lim_i$最大)(有可能不存在這個字符那么$lim_i=0$)
這個$lim_i$可以不斷地在$S$的后綴自動機上跳來求出。當無法向下匹配時,一直跳parent樹直到可以匹配為止
我們假設對於$T$的后綴自動機,每一個節點的$endpos$集合中所能代表的最長的字符串長度為$l_i$,$tag_i$表示該集合字符串第一次出現的結尾位置(因為集合里字符串互為后綴所以結尾相同),$fa_i$表示parent樹上的父親,$cnt$表示自動機節點總個數
那么答案就是$$ans=\sum_{i=2}^{cnt}max(0,l_i-max(l_{fa_i},lim_{tag_i}))$$
ps:這里的lim指的並不是上文的lim而是最長后綴的長度
上面式子的意思是,對於每一個節點,它不屬於$S$的子串的總個數為當前節點代表的集合字符串個數減去與$S$有匹配的子串個數
然后只要在$T$的后綴自動機上枚舉每一個節點就可以了
現在來考慮$l$和$r$任意的情況該怎么做
這個時候就要用線段樹維護后綴自動機的$endpos$集合了(不明白這個怎么做的我簡單說一下,就是搞一個動態開點線段樹,如果一個節點的$endpos$集合里有某一個位置就把它加入以該點為根的樹中,parent樹上父親節點的$endpos$集合必然包含兒子的$endpos$集合所以將每個點的$endpos$集合與它兒子的合並。然后查詢這個節點是否有$endpos$位於某個區間中只要在線段樹上查詢看看這個區間代表的節點是否被開出來過就好了(因為線段樹上只有存在的位置的節點被開出來過))
我們在處理$lim_i$集合的時候要注意,要判斷當前節點是否在$S[l..r]$區間中出現過。設$p$為當前在$S$的自動機上跑到的節點,$len$表示匹配了$[i-len+1..i]$,因為要看能否轉移到下一個節點,所以要匹配$[i-len,i]$,且$[l+len,r]$中有后繼節點的$endpos$存在才行(可能說的煩了點,仔細想想為什么)
差不多就這樣
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #define ll long long 7 using namespace std; 8 char sr[1<<21],z[20];int C=-1,Z; 9 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 10 inline void print(ll x){ 11 if(C>1<<20)Ot(); 12 while(z[++Z]=x%10+48,x/=10); 13 while(sr[++C]=z[Z],--Z);sr[++C]='\n'; 14 } 15 const int N=1e6+5,M=2e7+5,inf=0x3f3f3f3f; 16 int q,n,m,rt[N],lim[N];char S[N];ll ans; 17 namespace tree{ 18 int cnt,L[M],R[M]; 19 void ins(int &p,int l,int r,int x){ 20 if(!p) p=++cnt; 21 if(l==r) return; 22 int mid=(l+r)>>1; 23 if(x<=mid) ins(L[p],l,mid,x); 24 else ins(R[p],mid+1,r,x); 25 } 26 int merge(int x,int y){ 27 if(!x||!y) return x+y; 28 int p=++cnt; 29 L[p]=merge(L[x],L[y]); 30 R[p]=merge(R[x],R[y]); 31 return p; 32 } 33 bool query(int p,int l,int r,int ql,int qr){ 34 if(!p||ql>qr) return false; 35 if(ql<=l&&qr>=r) return true; 36 int mid=(l+r)>>1; 37 if(ql<=mid&&query(L[p],l,mid,ql,qr)) return true; 38 if(qr>mid&&query(R[p],mid+1,r,ql,qr)) return true; 39 return false; 40 } 41 } 42 namespace SAM{ 43 int cnt=1,last=1; 44 int ch[N][26],l[N],fa[N],c[N],a[N],in[N]; 45 void ins(int c){ 46 int p=last,np=++cnt;last=np,l[np]=l[p]+1,in[np]=1; 47 for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; 48 if(!p) fa[np]=1; 49 else{ 50 int q=ch[p][c]; 51 if(l[q]==l[p]+1) fa[np]=q; 52 else{ 53 int nq=++cnt;l[nq]=l[p]+1; 54 memcpy(ch[nq],ch[q],sizeof(int)*(26)); 55 fa[nq]=fa[q],fa[q]=fa[np]=nq; 56 for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 57 } 58 } 59 } 60 inline void calc(){ 61 for(int i=1;i<=n;++i) ins(S[i]-'a'); 62 for(int i=1;i<=cnt;++i) ++c[l[i]]; 63 for(int i=1;i<=cnt;++i) c[i]+=c[i-1]; 64 for(int i=1;i<=cnt;++i) a[c[l[i]]--]=i; 65 for(int i=cnt,p;i;--i){ 66 p=a[i]; 67 if(in[p]) tree::ins(rt[p],1,n,l[p]); 68 rt[fa[p]]=tree::merge(rt[fa[p]],rt[p]); 69 } 70 } 71 } 72 namespace solve{ 73 int cnt=1,last=1; 74 int ch[N][26],l[N],fa[N],c[N],a[N],tag[N]; 75 inline void init(){ 76 cnt=last=1,memset(ch[1],0,sizeof(int)*(26)); 77 } 78 inline int newnode(){ 79 ++cnt;memset(ch[cnt],0,sizeof(int)*(26));return cnt; 80 } 81 void ins(int c){ 82 int p=last,np=newnode();last=np,tag[np]=l[np]=l[p]+1; 83 for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; 84 if(!p) fa[np]=1; 85 else{ 86 int q=ch[p][c]; 87 if(l[q]==l[p]+1) fa[np]=q; 88 else{ 89 int nq=newnode();l[nq]=l[p]+1,tag[nq]=tag[q]; 90 memcpy(ch[nq],ch[q],sizeof(int)*(26)); 91 fa[nq]=fa[q],fa[q]=fa[np]=nq; 92 for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 93 } 94 } 95 } 96 ll solve(){ 97 int L,R; 98 scanf("%s%d%d",S+1,&L,&R); 99 init(); 100 m=strlen(S+1); 101 for(int len=0,p=1,i=1;i<=m;++i){ 102 int c=S[i]-'a'; 103 ins(c); 104 while(true){ 105 if(SAM::ch[p][c]&&tree::query(rt[SAM::ch[p][c]],1,n,L+len,R)){ 106 ++len,p=SAM::ch[p][c];break; 107 } 108 if(len==0) break; 109 --len; 110 if(len==SAM::l[SAM::fa[p]]) p=SAM::fa[p]; 111 } 112 lim[i]=len; 113 } 114 ans=0; 115 for(int i=2;i<=cnt;++i) 116 ans+=max(0,l[i]-max(l[fa[i]],lim[tag[i]])); 117 return ans; 118 } 119 } 120 int main(){ 121 // freopen("testdata.in","r",stdin); 122 scanf("%s",S+1); 123 n=strlen(S+1); 124 SAM::calc(); 125 scanf("%d",&q); 126 while(q--) print(solve::solve()); 127 Ot(); 128 return 0; 129 }
