原题:https://www.luogu.org/problemnew/show/P2414
题解:给出一个字符串,从中可以拆出n个模式串,m组询问:第x个字符串在第y个字符串中出现了几次。暴力的做法,对于y的前缀,查询失败指针中x出现了几次。很容易想到离线的做法将y排序,查找y前缀的失败指针,用桶维护一下,可以拿到70分。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10;
const int Z=26;
struct node{int a,b,p;}b[N];
char s[N],stack[N];
int top,cnt,tot,m,ans[N],ch[N][Z],fa[N],nxt[N],end[N],f[N],sum[N];
bool mark[N];
bool cmp(node x,node y){
return x.b==y.b?x.a<y.a:x.b<y.b;
}
inline void ins(char *s,int ind,int len){
int p=1;
for(int i=1;i<=len;i++){
int c=s[i]-'a';
if(!ch[p][c]) ch[p][c]=++tot;
fa[ch[p][c]]=p;
p=ch[p][c];
}
mark[p]=1;end[ind]=p;f[p]=ind;
}
void bfs(){
queue<int> q;
for(int i=0;i<Z;i++) ch[0][i]=1;
q.push(1);nxt[1]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<Z;i++){
if(!ch[u][i]) ch[u][i]=ch[nxt[u]][i];
else{
q.push(ch[u][i]);
int v=nxt[u];
while(v>0 && !ch[v][i]) v=nxt[v];
nxt[ch[u][i]]=ch[v][i];
}
}
}
}
inline int query(int x,int y){
int ans=0;
for(int k=end[y];k>1;k=fa[k]){
for(int p=k;p>1;p=nxt[p])
if(p==end[x]){ans++;break;}
}
return ans;
}
int main(){
// freopen("type2.in","r",stdin);
scanf("%s",s+1);
top=0;cnt=0;tot=1;
for(int i=1;i<=strlen(s+1);i++){
if(s[i]>='a' && s[i]<='z') stack[++top]=s[i];
if(s[i]=='B') top--;
if(s[i]=='P'){
cnt++;ins(stack,cnt,top);
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&b[i].a,&b[i].b),b[i].p=i;
bfs();
sort(b+1,b+m+1,cmp);
int last=-1;
for(int i=1;i<=m;i++){
int x=b[i].a;int y=b[i].b;
if(last!=y) {
memset(sum,0,sizeof sum);
for(int k=end[y];k>1;k=fa[k])
for(int p=k;p>1;p=nxt[p]) if(f[p]) sum[f[p]]++;
last=y;
}
ans[b[i].p]=sum[x];
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}
对于每个y都要查所有的失败指针显然是不能接受的,将失败指针反向,对于x的子树中有多少是y的前缀就是答案,可以做深搜序,差分,用树状数组维护区间的和来解决。同时注意建字典树时可以根据第一行的字符串来进行点修改。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+1000;
const int Z=26;
struct node{int a,b,p;}b[N];
struct E{
int x,y,next;
}mm[N];
char s[N];
int ans[N],ch[N][Z],fa[N],nxt[N],end[N],f[N],h[N],l[N],r[N],bit[N];
int nn,top,cnt,tot,m,num;
bool mark[N],vis[N];
bool cmp(node x,node y){return x.b==y.b?x.a<y.a:x.b<y.b;}
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int c){for(x;x<=num;x+=lowbit(x)) bit[x]+=c;}
inline int get(int x){
int ans=0;
for(x;x>=1;x-=lowbit(x)) ans+=bit[x];
return ans;
}
void bfs(){
queue<int> q;
for(int i=0;i<Z;i++) ch[0][i]=1;
q.push(1);nxt[1]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<Z;i++){
if(!ch[u][i]) ch[u][i]=ch[nxt[u]][i];
else{
q.push(ch[u][i]);
int v=nxt[u];
while(v>0 && !ch[v][i]) v=nxt[v];
nxt[ch[u][i]]=ch[v][i];
}
}
}
}
inline void insE(int x,int y){mm[++nn].x=x;mm[nn].y=y;mm[nn].next=h[x];h[x]=nn;}
void dfs(int u){
l[u]=++num; vis[u]=1;
for(int k=h[u];k;k=mm[k].next){
int v=mm[k].y;
if(!vis[v]) dfs(v);
}
r[u]=num;
return ;
}
int main(){
// freopen("type2.in","r",stdin);
// freopen("test.out","w",stdout);
top=0;cnt=0;tot=1;memset(h,0,sizeof h);nn=0;num=0;
scanf("%s",s+1);
for(int i=1,p=1;i<=strlen(s+1);i++){
if(s[i]>='a' && s[i]<='z'){
int c=s[i]-'a';
if(!ch[p][c]) ch[p][c]=++tot;
fa[ch[p][c]]=p;
p=ch[p][c];
}
if(s[i]=='B') p=fa[p];
if(s[i]=='P'){
cnt++;end[cnt]=p;f[p]=cnt;
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&b[i].a,&b[i].b),b[i].p=i;
bfs();
for(int i=2;i<=tot;i++) insE(nxt[i],i);//,printf("%d %d\n",i,nxt[i]);
dfs(1);
sort(b+1,b+m+1,cmp);
for(int i=1,j=1,now=0,p=1;i<=strlen(s+1);i++){
if(s[i]=='P'){
now++;
for(;j<=m&&b[j].b<=now;j++) {
int x=b[j].a;
ans[b[j].p]=(get(r[end[x]])-get(l[end[x]]-1));
}
}
if(s[i]=='B'){add(l[p],-1);p=fa[p];}
if(s[i]>='a' && s[i]<='z')p=ch[p][s[i]-'a'],add(l[p],1);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}