BZOJ2434 [Noi2011]阿狸的打字机
AC自动机+树状数组
先建出fail树,对于查询x在y中出现几次,就等于在x为根的子树下有多少个节点为单词y在tire树路径上所在的节点,可用dfs+树状数组离线求出答案。
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<vector>
#define pb push_back
#define mp make_pair
#define fi first
#define sc second
#define N 1000010
using namespace std;
char s[N];
int tmp,len,i,fa[N],cnt,p[N],tot,fail[N],c,a,b,ans[N];
int C[N];
int f[N][27],ss,L[N],R[N],m;
int dp,pre[N],tt[N],query[N];
queue<int> q;
vector<pair<int,int> > vec[N];
int lowbit(int x)
{
return x&(-x);
}
void cc(int x,int w)
{
while (x<=ss)
{
C[x]+=w;
x+=lowbit(x);
}
}
int sum(int x)
{
int ans=0;
while (x>0)
{
ans+=C[x];
x-=lowbit(x);
}
return ans;
}
void link(int x,int y)
{
dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;
}
void dfs(int x)
{
int i=p[x];
ss++;L[x]=ss;
while (i)
{
dfs(tt[i]);
i=pre[i];
}
R[x]=ss;
}
void solve()
{
int i,tmp=0,cnt=0,j;
for (i=0;i<len;i++)
if (s[i]=='B')
{
cc(L[tmp],-1);
tmp=fa[tmp];
}
else
if (s[i]=='P')
{
cnt++;
for (j=0;j<vec[cnt].size();j++)
{
int o=query[vec[cnt][j].fi];
ans[vec[cnt][j].sc]=sum(R[o])-sum(L[o]-1);
}
}
else
{
tmp=f[tmp][s[i]-97];
cc(L[tmp],1);
}
}
int main()
{
scanf("%s",s);
len=strlen(s);
tmp=0;
for (i=0;i<len;i++)
if (s[i]=='B')
tmp=fa[tmp];
else
if (s[i]=='P')
{
cnt++;
query[cnt]=tmp;
}
else
{
if (f[tmp][s[i]-97]==0)
{
f[tmp][s[i]-97]=++tot;
fa[tot]=tmp;
}
tmp=f[tmp][s[i]-97];
}
for (i=0;i<26;i++)
if (f[0][i])
{
fail[f[0][i]]=0;
q.push(f[0][i]);
}
while (!q.empty())
{
c=q.front();q.pop();
for (i=0;i<26;i++)
if (f[c][i]==0)
f[c][i]=f[fail[c]][i];
else
{
fail[f[c][i]]=f[fail[c]][i];
q.push(f[c][i]);
}
}
for (i=1;i<=tot;i++)
link(fail[i],i);
dfs(0);
scanf("%d",&m);
for (i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
vec[b].pb(mp(a,i));
}
solve();
for (i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
浙公网安备 33010602011771号