【題解】2021CCPC華為雲挑戰賽 重疊的子串
阿新 • • 發佈:2021-08-22
題意
給定一個只含小寫字母的字串\(s\)和\(q\)次詢問,每次詢問給定一個字串,以\(s[l...r]\)的形式給出,判斷\(s\)中是否存在兩個或多個出現的有重疊部分的給定子串。比如在“ababa”中,兩個 “aba” 子串就重疊於中間的字母 “a”,而兩個 “ab” 子串就沒有發生重疊。\(T\)組資料。
\(1\le n\le10^5,1\le q\le 10^6,1\le T\le 20,\sum n\le 6\times 10^5,\sum q\le 3\times 10^6\)
題解
對\(s\)建\(SAM\),用set
+啟發式合併維護\(parent\)
set
中加入節點,所以容易維護每個節點\(u\)的\(endpos\)集合中任意兩個元素的最小差值,記為\(ans[u]\),只需在插入時用當前元素和其前後兩個元素的差值更新當前節點的答案。對於每個詢問,先倍增跳到\(parent\)樹上對應的節點\(u\)上,再比較串長和\(ans[u]\)即可。
#include <bits/stdc++.h> #define pb(x) emplace_back(x) using namespace std; using P=pair<int,int>; typedef long long ll; const int N=200005,M=26; char S[N]; int n,m,x,y; vector<int> e[N]; int anc[N][21],ans[N],id[N]; set<int> s[N]; struct sam{ int fa[N],len[N],lst,gt,ch[N][M],pos[N]; void init(){gt=lst=1;} void init2(){ for(int p=1;p<=gt;p++){ id[p]=0;ans[p]=n+1;fa[p]=0; e[p].clear(); s[p]=move(set<int>{}); for(int i=0;i<M;i++)if(ch[p][i])ch[p][i]=0; } lst=gt=1; } void ins(int c,int id){ int f=lst,p=++gt;lst=p; len[p]=len[f]+1;pos[p]=id; while(f&&!ch[f][c])ch[f][c]=p,f=fa[f]; if(!f){fa[p]=1;return ;} int x=ch[f][c],y=++gt; if(len[x]==len[f]+1){gt--;fa[p]=x;return ;} len[y]=len[f]+1;fa[y]=fa[x];pos[y]=pos[x];fa[x]=fa[p]=y; for(int i=0;i<M;i++)ch[y][i]=ch[x][i]; while(f&&ch[f][c]==x)ch[f][c]=y,f=fa[f]; } void f1(){ for(int u=gt;u>=1;u--){ anc[u][0]=fa[u]; e[fa[u]].pb(u); } dfs(1); id[0]=1; for(int i=1;i<=n;i++){ int c=S[i]-'a'; id[i]=ch[id[i-1]][c]; } } void dfs(int u){ int &tmp=ans[u];tmp=n+1; for(int i=1;i<=20;i++){ anc[u][i]=anc[anc[u][i-1]][i-1]; } for(auto v:e[u]){ dfs(v); tmp=min(tmp,ans[v]); if(u==1){continue;} if(s[u].size()>=s[v].size()){ for(auto j:s[v]){ auto it=s[u].insert(j).first; if(it!=s[u].begin()){ --it; tmp=min(tmp,j-*it); ++it; } if((++it)!=s[u].end()){ tmp=min(tmp,*it-j); } } } else{ for(auto j:s[u]){ auto it=s[v].insert(j).first; if(it!=s[v].begin()){ --it; tmp=min(tmp,j-*it); ++it; } if((++it)!=s[v].end()){ tmp=min(tmp,*it-j); } } s[u]=move(s[v]); } } } }g; void f1(){ int Q; scanf("%d%d%s",&n,&Q,S+1); g.init2(); for(int i=1;i<=n;i++){ g.ins(S[i]-'a',i); s[g.lst].insert(i); } g.f1(); for(int i=1;i<=Q;i++){ int x,y;scanf("%d%d",&x,&y); int p=id[y],l=y-x+1; for(int i=20;i>=0;i--){ int na=anc[p][i]; if(na>1&&g.len[na]>=l){ p=na; } } if(ans[p]<l){ printf("Yes\n"); } else{ printf("No\n"); } } } int main(){ int t;scanf("%d",&t); while(t--){ f1(); } return 0; }