多次询问可以按时间分治,但可惜这题强制在线。
因而引入了二进制分组,就是把当前字符串的数量二进制拆分。
比如说当前有10个字符串,就把这10个字符串分成一组8个和一组2个。
这样每个字符串最多被重构AC自动机
log2n
,一种优美的暴力
二进制分组适用于那些不支持修改的数据结构,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;
}