这题可以大大加深我们对自动AC机的理解。
一上来:哇,这是什么神仙读入啊,如果按照它的要求一个一个把所有串建出来的话,肯定会MLE呀!
后来想一想,它这么读入,肯定构成一棵树,并且,它刚好是字典树!
这里是建树的方法。
void ins(){
int x=1;
for(int i=0;i<S;i++){
if(s[i]=='P')t[x].fin.push_back(++n),pos[n]=x;
else if(s[i]=='B'){
if(x==1)continue;
x=t[x].fa;
}else{
if(!t[x].ch[s[i]-'a'])t[x].ch[s[i]-'a']=++cnt,t[cnt].fa=x,t[x].org[s[i]-'a']=true;
x=t[x].ch[s[i]-'a'];
}
}
}
注意到这里有一个将串映射到trie中节点的数组(pos),以后有用。
然后就不会了,看了题解。
我们必须明确一点,就是一个节点的所有祖先(fail)内,包含了它的全部出现过的前缀。
因此,对于询问((x,y)),我们找到(x)串的结尾节点,则该节点在(fail)树中的子树内,有多少个节点出现在了(y)串中,则询问((x,y))的答案就是多少。
因为对于每一个这样的节点,它总可以通过多次跳(fail)跳到(x)串的结尾节点上。
但是,就算这样,我们也无法非常便捷地求答案。
考虑转换思路,以(y)串为枚举对象,查询有多少节点在(x)串的结尾节点的子树内。
查询子树内信息,我们有经典结构dfs序。
我们可以将询问离线下来,并将询问存储在(y)节点的位置上。然后在trie树上爆搜(注意是trie树,不是trie图,在求(fail)时连上的新点不能进入,不然会挂),每到一个节点就统计(以当前节点到根所表示的字符串)作为(y)的答案。
在dfs序上单点修改,区间求和,我们有树状数组。
关键代码(爆炸警告):
for(int j=0;j<v[t[x].fin[i]].size();j++)
res[v[t[x].fin[i]][j].second]=
ask(t[pos[v[t[x].fin[i]][j].first]].dfn+t[pos[v[t[x].fin[i]][j].first]].sz-1)-
ask(t[pos[v[t[x].fin[i]][j].first]].dfn-1);
(v)是一个(vector),存的是询问,其中(first)是(x)串,(second)是序号。
(res)是统计答案的数组。
(ask)是树状数组的前缀和。
(dfn)是dfs序。
(sz)是(fail)树中子树大小。
注意哪里是(fail)树,哪里是trie树,哪里是trie图!!
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,S,cnt=1,head[100100],lim,pos[100100],BIT[100100],res[100100];
char s[100100];
struct Edge{
int to,next;
}edge[200100];
void ae(int u,int v){
static int tot;
edge[tot].next=head[u],edge[tot].to=v,head[u]=tot++;
}
void add(int x,int val){
while(x<=lim)BIT[x]+=val,x+=x&-x;
}
int ask(int x){
int sum=0;
while(x)sum+=BIT[x],x-=x&-x;
return sum;
}
struct AC_automaton{
int ch[26],fa,fail,dfn,sz;
bool org[26];
vector<int>fin;
}t[100100];
void ins(){
int x=1;
for(int i=0;i<S;i++){
if(s[i]=='P')t[x].fin.push_back(++n),pos[n]=x;
else if(s[i]=='B'){
if(x==1)continue;
x=t[x].fa;
}else{
if(!t[x].ch[s[i]-'a'])t[x].ch[s[i]-'a']=++cnt,t[cnt].fa=x,t[x].org[s[i]-'a']=true;
x=t[x].ch[s[i]-'a'];
}
}
}
vector<pair<int,int> >v[100100];
queue<int>q;
void build(){
for(int i=0;i<26;i++){
if(!t[1].ch[i])t[1].ch[i]=1;
else t[t[1].ch[i]].fail=1,q.push(t[1].ch[i]),ae(1,t[1].ch[i]);
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;i++){
if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]),ae(t[t[x].fail].ch[i],t[x].ch[i]);
else t[x].ch[i]=t[t[x].fail].ch[i];
}
}
}
void getdfn(int x){
// printf("%d
",x);
t[x].dfn=++lim,t[x].sz=1;
for(int i=head[x];i!=-1;i=edge[i].next)getdfn(edge[i].to),t[x].sz+=t[edge[i].to].sz;
// printf("%d
",x);
}
void dfs(int x){
add(t[x].dfn,1);
for(int i=0;i<t[x].fin.size();i++){
// printf("%d:%d
",x,t[x].fin[i]);
for(int j=0;j<v[t[x].fin[i]].size();j++)res[v[t[x].fin[i]][j].second]=ask(t[pos[v[t[x].fin[i]][j].first]].dfn+t[pos[v[t[x].fin[i]][j].first]].sz-1)-ask(t[pos[v[t[x].fin[i]][j].first]].dfn-1);
}
for(int i=0;i<26;i++)if(t[x].org[i])dfs(t[x].ch[i]);
add(t[x].dfn,-1);
}
int main(){
scanf("%s",s),S=strlen(s),memset(head,-1,sizeof(head)),ins(),build(),getdfn(1);
scanf("%d",&m);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[y].push_back(make_pair(x,i));
dfs(1);
for(int i=1;i<=m;i++)printf("%d
",res[i]);
return 0;
}