[hdu 6096 String](巧妙建圖,AC自動機)
阿新 • • 發佈:2021-08-11
題意:
給n個字串,和q個詢問,每個詢問給一個字首和字尾,問你在這n個字串中有多少個包含這一對字首和字尾,字首字尾不能重疊。
題解:
這題有一個巧妙的辦法,用AC自動機去跑。
比如待匹配串是abc,abcd,那麼我們將它們轉換為abc{abc,abcd{abcd,
為什麼用'{'呢,因為ascll表裡面{位於z的後面,[位於Z的後面,這樣比用'#'更方便。
ps:我一開始溢位了,這vscode居然不給我報錯。。。。
然後詢問是a c和ab cd ,那麼就將詢問轉換為c{a和cd{ab。
然後將詢問插進AC自動機,將每個帶匹配串拿去匹配一下,同時注意a 匹配 a a,這裡重合了,所以每次匹配要判斷長度。
code:
#include<bits/stdc++.h> using namespace std; #define N 100003 int n,tot,qq,ans_temp[N],Ans[N],LEN[N]; string T[N]; struct trie{ int son[29],fail,val,id; }tr[N*51]; queue<int>Q; void insert(){ string S,B; int root=0,tot=0; memset(tr[0].son,0,sizeof(tr[0].son)); tr[0].fail=tr[0].val=tr[0].id=0; for(int i=1;i<=qq;i++){ ans_temp[i]=i; Ans[i]=0; } for(int i=1;i<=qq;i++){ root=0; cin>>S>>B; B=B+'{'; B=B+S; S=B; int lens=S.length(); for(int p=0;p<lens;++p){ int k=S[p]-'a'; // cout<<root<<"_"<<k<<endl; if(!tr[root].son[k]){ memset(tr[tot+1].son,0,sizeof(tr[tot+1].son)); tr[tot+1].fail=tr[tot+1].val=tr[tot+1].id=0; tr[root].son[k]=++tot; } root=tr[root].son[k]; } if(!tr[root].id) tr[root].id=i; else ans_temp[i]=tr[root].id; LEN[tr[root].id]=lens; } } void makefail(){ while(!Q.empty())Q.pop(); for(int i=0;i<27;i++){ if(tr[0].son[i]){ tr[tr[0].son[i]].fail=0; Q.push(tr[0].son[i]); } } while(!Q.empty()){ int u=Q.front(); Q.pop(); for(int i=0;i<27;++i){ if(tr[u].son[i]){ tr[tr[u].son[i]].fail=tr[tr[u].fail].son[i]; Q.push(tr[u].son[i]); } else tr[u].son[i]=tr[tr[u].fail].son[i]; } } } void query(string t){ int le=t.length(),u=0,k; for(int i=0;i<le;i++) { k=t[i]-'a';u=tr[u].son[k]; for(int p=u;p;p=tr[p].fail) { if(((le-2)>>1)>=LEN[tr[p].id]-1){ Ans[tr[p].id]++; } } } return ; } int main(){ int Ti; cin>>Ti; while(Ti--){ cin>>n>>qq; for(int i=1;i<=n;i++) cin>>T[i]; insert(); makefail(); for(int i=1;i<=n;i++){ T[i]=T[i]+'{'; T[i]=T[i]+T[i]; query(T[i]); } for(int i=1;i<=qq;i++)printf("%d\n",Ans[ans_temp[i]]); } }