HDU - 7091 重叠的子串(后缀自动机+set启发式合并+树上倍增)
題目鏈接:點擊查看
題目大意:給定一個只含小寫字母的字符串 sss 和 qqq 次詢問,每次詢問給定一個字符串,以 s[l..r]s[l..r]s[l..r] 的形式給出,判斷 sss 中是否存在兩個或多個出現的有重疊部分的給定子串。比如在 “ababa”“ababa”“ababa” 中,兩個 “aba”“aba”“aba” 子串就重疊于中間的字母 “a”“a”“a”,而兩個 “ab”“ab”“ab” 子串就沒有發生重疊。TTT 組數據。
題目分析:對字符串 sss 建立 SAMSAMSAM 然后建出 parerentparerentparerent 樹,問題就轉換為了子串 s[l:r]s[l:r]s[l:r] 所在的節點,是否存在著相鄰的兩個 endposendposendpos 距離小于 r?l+1r-l+1r?l+1
針對上面的問題,可以用 setsetset 啟發式合并預處理出任意一個節點中相鄰兩個 endposendposendpos 的最小值,時間復雜度是 O(nlog2n)O(nlog^2n)O(nlog2n) 的
對于每次查詢,我們只需要從 s[1:r]s[1:r]s[1:r] 不斷跳 fatherfatherfather 直到 s[l:r]s[l:r]s[l:r] 所在的區間就可以了,這里可以用樹上倍增優化,時間復雜度是 O(qlogn)O(qlogn)O(qlogn) 的
代碼:
// Problem: 重疊的子串 // Contest: HDOJ // URL: https://acm.hdu.edu.cn/showproblem.php?pid=7091 // Memory Limit: 524 MB // Time Limit: 20000 ms // // Powered by CP Editor (https://cpeditor.org)// #pragma GCC optimize(2) // #pragma GCC optimize("Ofast","inline","-ffast-math") // #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> #include<list> #include<unordered_map> #define lowbit(x) (x&-x) using namespace std; typedef long long LL; typedef unsigned long long ull; template<typename T> inline void read(T &x) {T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f; } template<typename T> inline void write(T x) {if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0'); } const int inf=0x3f3f3f3f; const int N=1e5+100; char s[N]; int tot,last,dp[N<<1][20],val[N<<1],pos[N]; vector<int>node[N<<1]; set<int>endpos[N<<1]; struct Node {int ch[26];int fa,len; }st[N<<1]; inline int newnode() {tot++;for(int i=0;i<26;i++)st[tot].ch[i]=0;st[tot].fa=st[tot].len=0;node[tot].clear(),endpos[tot].clear();val[tot]=inf;return tot; } void add(int x) {int p=last,np=last=newnode();st[np].len=st[p].len+1;while(p&&!st[p].ch[x])st[p].ch[x]=np,p=st[p].fa;if(!p)st[np].fa=1;else{int q=st[p].ch[x];if(st[p].len+1==st[q].len)st[np].fa=q;else{int nq=newnode();st[nq]=st[q]; st[nq].len=st[p].len+1;st[q].fa=st[np].fa=nq;while(p&&st[p].ch[x]==q)st[p].ch[x]=nq,p=st[p].fa;//向上把所有q都替換成nq}} } void dfs(int u,int fa) {dp[u][0]=fa;for(int i=1;i<20;i++) {dp[u][i]=dp[dp[u][i-1]][i-1];}for(auto v:node[u]) {dfs(v,u);val[u]=min(val[u],val[v]);if(endpos[u].size()<endpos[v].size()) {swap(endpos[u],endpos[v]);}for(auto it:endpos[v]) {auto itt=endpos[u].lower_bound(it);if(itt!=endpos[u].end()) {val[u]=min(val[u],*itt-it);}if(itt!=endpos[u].begin()) {itt--;val[u]=min(val[u],it-*itt);}endpos[u].insert(it);}} } void build() {for(int i=1;i<=tot;i++) {node[st[i].fa].push_back(i);}dfs(1,0); } int get_fa(int u,int len) {for(int i=19;i>=0;i--) {if(st[dp[u][i]].len>=len) {u=dp[u][i];}}return u; } void init() {last=1;tot=0;newnode(); } int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int w;cin>>w;while(w--) {init();int n,m;scanf("%d%d%s",&n,&m,s+1);for(int i=1;i<=n;i++) {add(s[i]-'a');pos[i]=last;endpos[last].insert(i);}build();while(m--) {int l,r;scanf("%d%d",&l,&r);int len=r-l+1;int fa=get_fa(pos[r],len);if(val[fa]<len) {puts("Yes");} else {puts("No");}}}return 0; }總結
以上是生活随笔為你收集整理的HDU - 7091 重叠的子串(后缀自动机+set启发式合并+树上倍增)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021HDU多校10 - 7084 P
- 下一篇: 洛谷 - P7771 【模板】欧拉路径(