【題目大意】
某人學習單詞,有2中操作。1:+w,這個人學些了單詞w;2:?p,這個人閱讀一篇文章p。對於每次閱讀,輸出其中學習了的單詞數,即字符串p中有多少個子串是出現過的。特別地,爲了使你的算法是在線的,輸入的所有串是加密的,如果前一個答案爲L,實際的串是輸出的串左移L次的那個串,默認初始答案爲L。
【思路】
如果只是一個詢問,那麼做法很簡單,就是很裸的AC自動機。但是不斷有1,2操作的時候呢?如果每次都重構AC自動機,因爲構造fail指針是需要把字典樹bfs一次的,效率太差。這個時候,想到一個分塊思想,另 limit = sqrt(10^5),我們構造兩個AC自動機,每次把學習的字母加到後一個AC自動機中,如果後者的串總長>=limit,就是後一個AC自動機合到第一個中。
這樣的話,查詢的效率O(sum(p))。構造AC自動機的效率,顯然,合併操作不會超過sqrt(10^5)次,一次合併效率最壞爲 10^5 ,而每次後一個AC自動機重構fail指針的效率總和最壞也不會超過n*sqrt(n)。
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=100011122;
const double INFF=1e100;
const double eps=1e-8;
const int mod=1000000007;
const int NN=210;
const int MM=5000010;
/* ****************** */
char ss[MM];
char s1[MM];
const int k_size=2;
const int LEN=100005; //子串最大長度
struct Tire_tree
{
int fail,ge,id;
int t_next[k_size];
int next[k_size];
void init()
{
id=0;
fail=-1;//我普遍習慣用-1表示空
memset(next,-1,sizeof(next));
}
}tire[LEN],tire1[LEN];
int tire_q[LEN]; //用bfs模擬dfs,處理fail指針
void tire_insert(char *str,int l,int root,Tire_tree* tire,int& tire_cnt)
{
int i,x,p=root;
for(i=0;i<l;i++)
{
x = str[i]-'0'; //把字符變成連續的數字
if(tire[p].next[x]==-1)
{
tire_cnt++;
tire[tire_cnt].init();
tire[p].next[x]=tire_cnt;
}
p = tire[p].next[x];
}
tire[p].id = 1;
}
//處理tire中的fail指針
void init_fail(int root,Tire_tree* tire)
{
int tail,head;
int i,fa,p=root;
head=1;
tail=0;
tire[root].fail=root;
tire[root].ge = 0;
for(i=0;i<k_size;i++)
{
if(tire[p].next[i]!=-1)
{
tire_q[++tail] = tire[p].next[i];
tire[ tire_q[tail] ].fail = root;
tire[p].t_next[i] = tire[p].next[i];
}
else
{
tire[p].t_next[i] = root;
// tire[p].next[i]=root;
}
}
while(head<=tail)
{
p = tire_q[head++];
fa = tire[p].fail;
tire[p].ge = tire[p].id + tire[fa].ge;
for(i=0;i<k_size;i++)
{
if(tire[p].next[i]!=-1)
{
tire_q[++tail] = tire[p].next[i];
tire[ tire_q[tail] ].fail = tire[fa].t_next[i];
tire[p].t_next[i] = tire[p].next[i];
}
else
{
// tire[p].next[i] = tire[fa].next[i];
tire[p].t_next[i] = tire[fa].t_next[i];
}
}
}
}
void dfs(int p,int cen,int root,int &tire_cnt)
{
if(tire1[p].id != 0)
{
tire_insert(ss,cen,root,tire,tire_cnt);
}
int i;
for(i = 0; i < k_size; i++)
{
if(tire1[p].next[i] != -1)
{
ss[cen] = i+'0';
dfs(tire1[p].next[i],cen+1,root,tire_cnt);
}
}
}
void merger(int root,int t_root,int &tire_cnt)
{
dfs(t_root,0,root,tire_cnt);
init_fail(root,tire);
}
void solve(char *str,int l,int root,Tire_tree *tire,LL &ans)
{
int i, x, p = root;
for(i = 0; i < l; i++)
{
x = str[i] - '0';
p = tire[p].t_next[x];
if(p==-1)
{
while(1);
}
ans += tire[p].ge;
}
}
void fun_yi(char *str,int l,char *temp,int x)
{
int i,j;
for(i = 0, j = l-x; i < x;i++, j++)
{
temp[j] = str[i];
}
for(i = x, j = 0; i < l; i++, j++)
{
temp[j] = str[i];
}
}
bool tire_find(char* str,int l,int root,Tire_tree* tire)
{
int i, x ,p = root;
for(i = 0; i < l; i++)
{
x = str[i] - '0';
if(tire[p].next[x]==-1)
return false;
p = tire[p].next[x];
}
return (tire[p].id!=0);
}
int main()
{
int cas, ee = 0;
int i, n, l, sum_l;
int limit = sqrt( 100000.0 );
int root1,root2;
int tire_cnt;
int tire_cnt1;
LL ans;
scanf("%d",&cas);
while(cas--)
{
printf("Case #%d:\n",++ee);
ans = 0;
sum_l = 0;
root1 = 0;
root2 = 0;
tire[root1].init();
tire1[root2].init();
tire_cnt = 0;
tire_cnt1 = 0;
init_fail(root1,tire);
init_fail(root2,tire1);
scanf("%d",&n);
for(i = 0; i < n; i++)
{
scanf("%s",ss);
l = strlen(ss);
fun_yi(ss+1,l-1,s1, (int)(ans%(l-1)) );
if(ss[0]=='+')
{
if( !tire_find(s1,l-1,root1,tire) )
{
tire_insert(s1,l-1,root2,tire1,tire_cnt1);
init_fail(root2,tire1);
}
sum_l += l-1;
if(sum_l >= limit)
{
merger(root1,root2,tire_cnt);
sum_l = 0;
tire_cnt1 = 0;
tire1[root2].init();
init_fail(root2,tire1);
}
}
else
{
ans = 0;
solve(s1,l-1,root1,tire,ans);
solve(s1,l-1,root2,tire1,ans);
printf("%I64d\n",ans);
}
}
}
return 0;
}