这题看题面感觉挺玄学的,但其实会挂链式hash就能暴力切了,就是纸老虎,考察选手的语文水平。不过三年没写挂链hash也应该写一下了……
首先模数设成自然溢出ull,然后挂链时的模数取2^24。然后就可以直接hash了。对于3操作直接O(Σ|S|)询问即可,对于1、2操作,直接暴力加、减长度不超过50的字符,毕竟k<=50,这是个关键性条件。所以暴力能切了。
下面分析时间复杂度:先假设没有2操作,因为每一个位置只有长度不超过50的,每次加的数量也是不超过50的,这样总复杂度是均摊O(nk)的;再考虑2操作,因为2操作数量很少,而且每次只会至多影响O(k2)个数,因此考虑2操作后,复杂度是O(ck2)的,于是总复杂度是O(nk+ck2+Σ|S|)。注意hash不能用map,要挂链,因为map自带大常数和log,为什么不能unordered_map呢?因为这是NOI,不能用c++11。

#include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int N=5e5+7,p=19260817,mod=998244353,gloid=(1<<24)-1; int n,m,q,a[N],pre[N],nxt[N],cnt[N],f[150]; ull h[150],pw[150]; char s[11000000]; struct Hash{ struct edge{ull x;int v,nxt;}e[21000000]; int hd[gloid+1],ecnt; void add(ull x,int d) { int u=x&gloid; for(int i=hd[u];i;i=e[i].nxt)if(e[i].x==x){e[i].v+=d;return;} e[++ecnt]=(edge){x,d,hd[u]},hd[u]=ecnt; } int query(ull x) { int u=x&gloid; for(int i=hd[u];i;i=e[i].nxt)if(e[i].x==x)return e[i].v; return 0; } }mp; void merge() { int x,y,L=51,R=50;scanf("%d%d",&x,&y); memset(f,0,sizeof f); for(int i=x;i&&L>1;i=pre[i])f[--L]=a[i]; for(int i=y;i&&R<100;i=nxt[i])f[++R]=a[i]; for(int i=1;i<=R;i++)h[i]=h[i-1]*p+f[i]; for(int i=L;i<=50;i++) for(int j=51;j<=min(R,i+49);j++) mp.add(h[j]-h[i-1]*pw[j-i+1],1); nxt[x]=y,pre[y]=x; } void split() { int x,y,L=51,R=50;scanf("%d",&x),y=nxt[x]; memset(f,0,sizeof f); for(int i=x;i&&L>1;i=pre[i])f[--L]=a[i]; for(int i=y;i&&R<100;i=nxt[i])f[++R]=a[i]; for(int i=1;i<=R;i++)h[i]=h[i-1]*p+f[i]; for(int i=L;i<=50;i++) for(int j=51;j<=min(R,i+49);j++) mp.add(h[j]-h[i-1]*pw[j-i+1],-1); nxt[x]=pre[y]=0; } int query() { scanf("%s",s+1); int len=strlen(s+1),ret=1,k; scanf("%d",&k); ull val=0; if(k==1) { for(int i=1;i<=len;i++)ret=1ll*ret*cnt[s[i]]%mod; return ret; } for(int i=1;i<=len;i++) { val=val*p+s[i]; if(i>k)val-=pw[k]*s[i-k]; if(i>=k)ret=1ll*ret*mp.query(val)%mod; } return ret; } int main() { scanf("%d%d",&n,&q); pw[0]=1;for(int i=1;i<=50;i++)pw[i]=pw[i-1]*p; for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]+='0',cnt[a[i]]++; while(q--) { int op;scanf("%d",&op); if(op==1)merge(); else if(op==2)split(); else printf("%d ",query()); } }