题目概述
有一台打字机,可以:
- 在字符串末尾插入一个小写字母。
- 删除字符串末尾的一个小写字母。
- 输出当前字符串。
还有
m
个询问,每个询问
解题报告
观察打字的过程,我们发现这其实就是在构造一棵Trie:
- 在字符串末尾插入一个小写字母 → 在当前节点 now 扩展一个字符。
- 删除字符串末尾的一个小写字母 → 将 now 回退到父亲节点。
先按照打字顺序处理出Trie,然后构造好AC自动机和
fail
树,那么一个询问
x,y
就是查找字符串
x
对应的节点沿着
所以我们可以用DFS序+树状数组来快速统计
既然如此我们考虑离线,先把所有与
y
相关的
- 在字符串末尾插入一个小写字母 → 在当前节点 now 扩展一个字符,将新的 now 节点标记为 1 。
- 删除字符串末尾的一个小写字母
→ 将 now 节点标记为 0 ,并回退到父亲节点。 - 输出当前字符串
→ 处理 now 节点对应字符串 y 的所有x ,答案为 x <script type="math/tex" id="MathJax-Element-34">x</script> 子树的权值和。
示例程序
代码有点乱……读者老爷凑合看看吧QAQ。
#include<cstdio>
using namespace std;
const int maxn=100000,maxm=100000,maxi=26;
int n,te,ans[maxm+5];
char s[maxn+5];
int E,lnk[maxn+5],qlnk[maxn+5],nxt[maxn+maxm+5],chd[maxn+maxm+5],w[maxn+maxm+5];
void Add(int x,int y) {chd[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}
void Add(int x,int y,int z) {chd[++E]=y;w[E]=z;nxt[E]=qlnk[x];qlnk[x]=E;}
int si=1,son[maxn+5][maxi],top,stk[maxn+5],ID[maxn+5];
int que[maxn+5],fai[maxn+5];
void make_AC()
{
top=0;
for (int i=1;s[i];i++)
if (s[i]=='B') top--; else
if (s[i]=='P') ID[stk[top]]=++n; else
{
int &u=son[stk[top]][s[i]-'a'];if (!u) u=si++;
stk[++top]=u;
}
int Head=0,Tail=0;
for (int i=0;i<maxi;i++) if (son[0][i])
que[++Tail]=son[0][i],fai[son[0][i]]=0;
while (Head!=Tail)
{
int x=que[++Head];
for (int i=0;i<maxi;i++)
{
if (!son[x][i]) {son[x][i]=son[fai[x]][i];continue;}
int u=son[x][i];que[++Tail]=u;fai[u]=son[fai[x]][i];
}
}
for (int i=1;i<si;i++) Add(fai[i],i);
}
int ti,Lt[maxn+5],Rt[maxn+5],L[maxn+5],R[maxn+5];
void Dfs(int x)
{
Lt[x]=++ti;if (ID[x]) L[ID[x]]=ti;
for (int j=lnk[x];j;j=nxt[j]) Dfs(chd[j]);
Rt[x]=ti;if (ID[x]) R[ID[x]]=ti;
}
int c[maxn+5];
void Update(int x,int tem) {for (int p=x;p<=ti;p+=p&-p) c[p]+=tem;}
int Sum(int x) {int sum=0;for (int p=x;p;p-=p&-p) sum+=c[p];return sum;}
void Solve()
{
top=0;
for (int i=1;s[i];i++)
if (s[i]=='B') Update(Lt[stk[top--]],-1); else
if (s[i]=='P')
{
for (int j=qlnk[ID[stk[top]]];j;j=nxt[j])
ans[w[j]]=Sum(R[chd[j]])-Sum(L[chd[j]]-1);
} else
{
top++;stk[top]=son[stk[top-1]][s[i]-'a'];
Update(Lt[stk[top]],1);
}
}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
scanf("%s%d",s+1,&te);make_AC();
for (int i=1;i<=te;i++)
{
int x,y;scanf("%d%d",&x,&y);
Add(y,x,i);
}
Dfs(0);Solve();
for (int i=1;i<=te;i++) printf("%d\n",ans[i]);
return 0;
}