【HDU】4787 GRE Words Revenge 二進位制分組+AC自動機
阿新 • • 發佈:2019-01-31
多次詢問可以按時間分治,但可惜這題強制線上。
因而引入了二進位制分組,就是把當前字串的數量二進位制拆分。
比如說當前有10個字串,就把這10個字串分成一組8個和一組2個。
這樣每個字串最多被重構AC自動機一種優美的暴力
二進位制分組適用於那些不支援修改的資料結構,AC自動機算一例,凸包也是。
附上AC程式碼:
#include <cstdio>
#include <cstring>
#include <set>
#include <string>
#include <queue>
using namespace std;
typedef long long ll;
const int N=2e5+10,M=30;
struct AC{
int lk[2],nt,c;
}ac[N];
int ti,m,len,scnt,ncnt,root[M],sz[M],top;
char s[5000010],t[5000010];
set <string> st;
string str[N];
queue <int> que;
ll ans;
inline int newnode(void){return (++ncnt,ac[ncnt].lk[0]=ac[ncnt].lk[1]=ac[ncnt].nt=ac[ncnt].c=0 ),ncnt;}
inline void build(int x){
ac[x].nt=x;
for (int i=0; i<2; ++i) if (ac[x].lk[i]) ac[ac[x].lk[i]].nt=x,que.push(ac[x].lk[i]); else ac[x].lk[i]=x;
while (!que.empty()){
int p=que.front();que.pop();
for (int i=0; i<2; ++i)
if (ac[p].lk[i]){
ac[ac[p].lk[i]].nt=ac[ac[p].nt].lk[i];
ac[ac[p].lk[i]].c+=ac[ac[ac[p].lk[i]].nt].c;
que.push(ac[p].lk[i]);
}
else ac[p].lk[i]=ac[ac[p].nt].lk[i];
}
return;
}
inline void ist(char *s){
if (st.count(s+1)) return;
st.insert(s+1);str[++scnt]=string(s+1);
sz[++top]=1;
while (top>1&&sz[top]==sz[top-1]) sz[--top]*=2,ncnt=root[top]-1;
root[top]=newnode();
for (int i=scnt-sz[top]+1; i<=scnt; ++i){
int p=root[top];
for (int j=0; j<str[i].size(); ++j){
if (!ac[p].lk[str[i][j]-'0']) ac[p].lk[str[i][j]-'0']=newnode();
p=ac[p].lk[str[i][j]-'0'];
}
++ac[p].c;
}
build(root[top]);
}
inline ll query(char *s){
ll ret=0;
for (int i=1; i<=top; ++i){
int p=root[i];
for (int j=1; s[j]; ++j)
p=ac[p].lk[s[j]-'0'],ret+=ac[p].c;
}
return ret;
}
int main(void){
for (int i=(scanf("%d",&ti),1); i<=ti; ++i){
scanf("%d",&m),printf("Case #%d:\n",i),ans=0;
ncnt=scnt=top=0,memset(root,0,sizeof root),memset(sz,0,sizeof sz),st.clear();
while (m--){
char c=getchar();while (c!='+'&&c!='?') c=getchar();
scanf("%s",t+1),len=strlen(t+1);
for (int i=1; i<=len; ++i) s[i]=t[(i+ans-1)%len+1];
s[len+1]=0;
if (c=='+') ist(s);
else printf("%d\n",ans=query(s));
}
}
return 0;
}