給定一個長度爲 \(n\) 的字符串 \(s\), \(m\) 對 \((l_i,r_i)\), 回答 \(q\) 個詢問. 每一個詢問會給定一個長度爲 \(k\) 的字符串 \(w\) 以及一對 \(L,R\), 求全部知足 \(i\in [L,R]\) 的 \(w[l_i:r_i]\) 在 \(s\) 中的出現次數之和.c++
\(n,m,k,q\le 1\times 10^5\), \(\sum |w|\le 1\times 10^5\).curl
這sb題直接SAM暴力tm有90分...然而考場上把 \(k\) 和 \(q\) 讀反了掛成10分...url
那個 \(\sum |w|\) 顯然就是 \(kq\), 因而 \(kq\le1\times 10^5\). 咱們一點都不天然地想到能夠根號分類.spa
當 \(k\le \sqrt m\) 的時候, 顯然 \(k^2q=O(m)k=O(m\sqrt m)\). 那麼咱們直接枚舉 \(w\) 的全部子串, 在 \(s\) 的SAM上倍增計算答案, 二分計算當前查詢區間中有多少個當前子串就能夠統計當前子串對答案的貢獻了. 時間複雜度是 \(O(k^2q\log n)=O(m\sqrt m\log n)\).code
當 \(k>\sqrt m\) 的時候, 顯然 \(kq=O(m)\Rightarrow q=O(\sqrt m)\). 因而咱們將全部 \((l_i,r_i)\) 分佈到 \(r_i\) 上, 而後邊在SAM上跑邊計算右端點爲當前位置的子串對答案的貢獻. 一次的複雜度是 \(O(k+m\log n)\). 總複雜度顯然是 \(O(m\sqrt m \log n)\).blog
而後就過了...字符串
#include <bits/stdc++.h> namespace rvalue{ const int MAXN=2e5+10; typedef long long intEx; int n; int q; int k; int m; int lg; int cnt=1; int last=1; int root=1; int s[MAXN]; int prt[MAXN]; int len[MAXN]; int size[MAXN]; char buf[MAXN]; int pprt[20][MAXN]; std::map<char,int> chd[MAXN]; void Extend(char); namespace BF1{ const int SQRN=350; std::vector<int> qpos[SQRN][SQRN]; int main(){ #ifdef IRECT puts("BF1"); #endif for(int i=0;i<m;i++){ int l,r; scanf("%d%d",&l,&r); qpos[l][r].push_back(i); } while(q--){ int a,b; scanf("%s%d%d",buf,&a,&b); int cur=root,curlen=0; intEx ans=0; for(int i=0;i<k;i++){ while(cur!=root&&!chd[cur].count(buf[i])){ cur=prt[cur]; curlen=std::min(curlen,len[cur]); } if(chd[cur].count(buf[i])){ cur=chd[cur][buf[i]]; ++curlen; int pos=cur; for(int plen=curlen;plen>=1;plen--){ while(len[prt[pos]]>=plen) pos=prt[pos]; auto& q=qpos[i-plen+1][i]; auto low=std::lower_bound(q.begin(),q.end(),a); auto up=std::upper_bound(q.begin(),q.end(),b); int cnt=up-low; // printf("%d len=%d cnt=%d\n",i,plen,cnt); ans+=1ll*cnt*size[pos]; } } } printf("%lld\n",ans); } return 0; } } namespace BF2{ std::vector<std::pair<int,int>> qlen[MAXN]; int main(){ #ifdef IRECT puts("BF2"); #endif for(int i=0;i<m;i++){ int l,r; scanf("%d%d",&l,&r); qlen[r].emplace_back(r-l+1,i); } while(q--){ int a,b; scanf("%s%d%d",buf,&a,&b); int cur=root,curlen=0; intEx ans=0; for(int i=0;i<k;i++){ while(cur!=root&&!chd[cur].count(buf[i])){ cur=prt[cur]; curlen=std::min(curlen,len[cur]); } if(chd[cur].count(buf[i])){ cur=chd[cur][buf[i]]; ++curlen; for(auto q:qlen[i]){ if(q.second<a||q.second>b) continue; if(curlen<q.first) continue; int pos=cur; for(int j=lg;j>=0;j--) if(len[pprt[j][pos]]>=q.first) pos=pprt[j][pos]; ans+=size[pos]; } } } printf("%lld\n",ans); } return 0; } } int main(){ scanf("%d%d%d%d",&n,&m,&q,&k); scanf("%s",buf); for(int i=0;i<n;i++) Extend(buf[i]); for(int i=1;i<=cnt;i++) s[i]=i; std::sort(s+1,s+cnt+1,[](int a,int b){return len[a]>len[b];}); for(int i=1;i<=cnt;i++){ size[prt[s[i]]]+=size[s[i]]; pprt[0][i]=prt[i]; } for(int j=1;(1<<j)<=cnt;j++){ lg=j; for(int i=1;i<=cnt;i++) pprt[j][i]=pprt[j-1][pprt[j-1][i]]; } if(1ll*k*k<=m) BF1::main(); else BF2::main(); return 0; } void Extend(char x){ int p=last; int np=++cnt; size[last=np]=1; len[np]=len[p]+1; while(p&&!chd[p].count(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; len[nq]=len[p]+1; chd[nq]=chd[q]; prt[nq]=prt[q]; prt[q]=nq; prt[np]=nq; while(p&&chd[p][x]==q) chd[p][x]=nq,p=prt[p]; } } } } int main(){ #if 0 freopen("string.in","r",stdin); freopen("string.out","w",stdout); #endif rvalue::main(); return 0; }