题意:
给定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 */