题意:
给定T个测试数据
n个操作
+ 插入单词
? 询问母串中有多少个子串 在上面出现过
( 子串被加密,即←移动L位 (L为上次询问的答案) )
分块思路:
因为模式串和母串交叉给出,正常来说应该是,每次询问前都要getFail,这样显然会超时)
所以我们用一个小型ac自动机 buf , 每次插入都插入到 buf 中,并重建一下buf 的getFail
若buf的节点数 > 2000,则把其中节点添加到 ac自动机上
时间复杂度为 O(n*sqrt(n)) ,详见tao哥题解:http://blog.csdn.net/no__stop/article/details/16823479
geiFail一次 时间为 当前自动机上的节点数
#pragma comment( linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define maxnode 511111
#define sigma_size 2
struct Trie{
int ch[maxnode][sigma_size];
bool val[maxnode];
int f[maxnode];
int sz;
void init(){
sz=1;
memset(ch,0,sizeof(ch));
memset(val, 0, sizeof(val));
memset(f,0,sizeof(f));
}
int idx(char c){ return c-'0'; }
int insert(char *s){
int u = 0;
for(int i = 0; s[i] ;i++){
int c = idx(s[i]);
if(!ch[u][c])
ch[u][c] = sz++;
u = ch[u][c];
}
val[u] = 1;
return u;
}
bool search(char *s){
int u = 0;
for(int i = 0; s[i] ;i++){
int c = idx(s[i]);
if(!ch[u][c]) return 0;
u = ch[u][c];
}
return val[u];
}
void getFail(){
queue<int> q;
for(int i = 0; i<sigma_size; i++)
if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int r = q.front(); q.pop();
for(int c = 0; c<sigma_size; c++){
int u = ch[r][c];
if(!u)continue;
q.push(u);
int v = f[r];
while(v && ch[v][c] == 0) v = f[v]; //沿失配边走上去 如果失配后有节点 且 其子节点c存在则结束循环
f[u] = ch[v][c];
}
}
}
int find(char *T){
int j = 0, ans = 0;
for(int i = 0; T[i] ; i++){
int c = idx(T[i]);
while(j && ch[j][c]==0) j = f[j];
j = ch[j][c];
int temp = j;
while(temp){ //沿失配边走 || 若沿失配边走时一定要节点为单词结尾则改成while(temp && val[temp])
ans += val[temp];
temp = f[temp];
}
}
return ans;
}
};
Trie ac, buf;
void dfs(int u, int v){
for(int i = 0;i < 2;i++){
if( buf.ch[v][i] )
{
int e2 = buf.ch[v][i];
if(! ac.ch[u][i])
{
memset(ac.ch[ac.sz], 0, sizeof(ac.ch[ac.sz]));
ac.ch[u][i] = ac.sz++;
}
int e1 = ac.ch[u][i];
ac.val[e1] |= buf.val[e2];
dfs(e1, e2);
}
}
}
void join(){
dfs(0, 0);
buf.init();
ac.getFail();
}
char s[6000000],temp[6000000];
int main(){
int Cas = 1, T, n;scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("Case #%d:\n",Cas++);
ac.init();
buf.init();
int L = 0;
while(n--){
scanf("%s",temp);
int len = strlen ( temp + 1 ) ;
s[0] = temp[0] ;
for (int i = 0 ; i < len ; i ++ )
s[i+1] = temp[1+(i+L%len+len)%len] ;
s[len+1] = '\0';
if(s[0] == '+'){
if( buf.search(s+1) || ac.search(s+1) )continue;//若单词已存在
buf.insert(s+1);
buf.getFail();
if(buf.sz > 2000) join();
}
else
{
L = buf.find(s+1) + ac.find(s+1);
printf("%d\n", L);
}
}
}
return 0;
}
/*
/*
99
10
+01
+110
?010
+110
+00
+0
?001001
?001001
+110110
?1101001101
6
+01
+110
+110
+00
+0
?001001
20
+101001011
?110100
+11010100
?0011001101
+111011
?00010011
+0111010110
+0000101
+0
+11000
?1
+1010101
+0001
+0110
+0111101111
?1100
+0111
+1001
?0110111011
?1010010100
10
+00
?010110100
+0100000100
+111
+000000
?0000110
+110
+00
+0011
?101001
99
+0
+1000100
+01
+0
?1110010011
ans:
case 1
1
7
7
11
case 2
8
*/