題解 CF547E 【Mike and Friends】
阿新 • • 發佈:2020-10-07
AC自動機+線段樹合併。
將所有字串插入AC自動機中,建出fail樹。
若 \(s\) 是 \(t\) 的子串,令 \(u\) 表示 \(s\) 的末尾字元在AC自動機上對應的結點。由於AC自動機的性質,那麼一定存在一個 \(t\) 在AC自動機中的結點,滿足它在fail樹中在 \(u\) 的子樹中。
那麼問題就轉化為:詢問 \(s_k\) 在AC自動機上的結束結點的子樹中,有多少結點屬於 \(s_{l \cdots r}\) (即插入Trie時經過的結點)。
這個問題可以用線段樹合併解決。
每個結點開一個以字串編號為下標的線段樹。插入字串 \(s_{id}\) 時,在經過的所有結點的線段樹中的 \(id\)
將詢問離線,線段樹合併到一個結點時,線上段樹上查詢區間和即可。
程式碼挺好寫的。
\(\text{Code}:\)
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <vector> #include <queue> #define Rint register int #define INF 0x3f3f3f3f using namespace std; typedef long long lxl; const int maxn=2e5+5,maxq=5e5+5; template <typename T> inline void read(T &x) { x=0;T f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} x*=f; } struct edge { int u,v,next; edge(int u,int v,int next):u(u),v(v),next(next){} edge(){} }e[maxn]; int head[maxn],ecnt; inline void add(int u,int v) { e[ecnt]=edge(u,v,head[u]); head[u]=ecnt++; } struct Segment_Tree { int tot; int sum[maxn<<5],ch[maxn<<5][2]; inline void update(int p) { sum[p]=sum[ch[p][0]]+sum[ch[p][1]]; } void modify(int &p,int l,int r,int ps,int d) { if(!p) p=++tot; sum[p]+=d; if(l==r) return; int mid=(l+r)>>1; if(ps<=mid) modify(ch[p][0],l,mid,ps,d); else modify(ch[p][1],mid+1,r,ps,d); } int query(int p,int l,int r,int L,int R) { if(!sum[p]||(L<=l&&r<=R)) return sum[p]; int mid=(l+r)>>1,ans=0; if(L<=mid) ans+=query(ch[p][0],l,mid,L,R); if(R>mid) ans+=query(ch[p][1],mid+1,r,L,R); return ans; } int merge(int x,int y) { if(!x||!y) return x|y; sum[x]+=sum[y]; ch[x][0]=merge(ch[x][0],ch[y][0]); ch[x][1]=merge(ch[x][1],ch[y][1]); return x; } }st; int rt[maxn]; struct querys { int l,r,id; querys(int l,int r,int id):l(l),r(r),id(id){} querys(){} }; int n,q,idx[maxn]; vector<querys> vec[maxn]; int ans[maxq]; int ch[maxn][30],tot; int fail[maxn]; inline void insert(const char *s,int id) { int len=strlen(s+1),u=0; for(int i=1;i<=len;++i) { int c=s[i]-'a'+1; if(!ch[u][c]) ch[u][c]=++tot; u=ch[u][c]; st.modify(rt[u],1,n,id,1); } idx[id]=u; } inline void GetFail() { queue<int> q; memset(fail,-1,sizeof(fail)); for(int i=1;i<=26;++i) if(ch[0][i]) q.push(ch[0][i]),fail[ch[0][i]]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=1;i<=26;++i) if(ch[u][i]) { fail[ch[u][i]]=ch[fail[u]][i]; q.push(ch[u][i]); } else ch[u][i]=ch[fail[u]][i]; } for(int i=1;i<=tot;++i) if(~fail[i]) add(fail[i],i); } inline void dfs(int u) { for(int i=head[u];~i;i=e[i].next) { int v=e[i].v; dfs(v); rt[u]=st.merge(rt[u],rt[v]); } for(auto v:vec[u]) ans[v.id]=st.query(rt[u],1,n,v.l,v.r); } char s[maxn]; int main() { // freopen("CF547E.in","r",stdin); read(n),read(q); for(int i=1;i<=n;++i) { scanf(" %s",s+1); insert(s,i); } memset(head,-1,sizeof(head)); GetFail(); for(int i=1,l,r,k;i<=q;++i) { read(l),read(r),read(k); vec[idx[k]].push_back(querys(l,r,i)); } dfs(0); for(int i=1;i<=q;++i) printf("%d\n",ans[i]); return 0; }