「雅礼集训 2017 Day1」字符串
SOL:
我们可以考虑SAM来跑这玩意。 我们发现 q*k=C是一个常数。
那么我们可以分段处理:当k小于一个值的时候,我们可以在sam上暴力求出当前串的每一个子串的出现次数。
复杂度O(kC+simga q)
当k很大时,我们在SAM的fail树上倍增,我们暴力记下每一个前缀,对于每个前缀通过fail树向前走找到其后缀,即任意一子串。复杂度O(C+C/k*mlogn )
这样写然而还是会T一个点,我们观察发现 simga q是限速步,我们考虑前缀和优化,强行套一颗主席树就好了:
复杂度O(kClog k)
然而本人比较懒,不想打主席树,面向数据把最后一个点水过去了。
#include<bits/stdc++.h> #define SIZ 21 #define N 210007 #define M 567 #define LL long long using namespace std; struct Sp{ int fa,ch[26],len,val; }; int f[N][SIZ],q,len,l[N],r[N],k,now,n,m,c[N],id[N],L,R; LL Ans; char w[N>>1]; struct SAM{ int tot=1,last=1,q,np,p; Sp T[N]; void add(int x){ q=++tot; T[q].len=T[last].len+1; for (;last&&!T[last].ch[x];last=T[last].fa) T[last].ch[x]=q; if (!last) T[q].fa=1; else { p=T[last].ch[x]; if (T[p].len==T[last].len+1) T[q].fa=p; else { np=++tot; T[np]=T[p]; T[np].val=0; T[np].len=T[last].len+1; T[q].fa=T[p].fa=np; for(;last&&T[last].ch[x]==p;last=T[last].fa) T[last].ch[x]=np; } } last=q; T[last].val=1; } void build() { for (int i=1;i<=tot;i++) f[i][0]=T[i].fa; for (int i=1;i<SIZ;i++) for (int j=1;j<=tot;j++) f[j][i]=f[f[j][i-1]][i-1]; } int que(int x,int val) { if (T[x].len<val) return 0; for (int i=SIZ-1;~i;i--) if (T[f[x][i]].len>=val) x=f[x][i]; return T[x].val; } }Sam; namespace work1 { int ans[M][M],gg[N][11][11];; int x; void solve() { if (k>10) { while (q--) { Ans=0; memset(ans,0,sizeof ans); scanf("%s%d%d",w+1,&L,&R); len=strlen(w+1); for (int i=1;i<=k;i++) { now=1; for (int j=i;j<=k;j++) { x=w[j]-'a'; if (!Sam.T[now].ch[x]) break; now=Sam.T[now].ch[x]; ans[i][j]=Sam.T[now].val; } } for (int i=L;i<=R;i++) Ans+=ans[l[i]][r[i]]; printf("%lld\n",Ans); } } else { for (int i=0;i<m;i++) { if (i) memcpy(gg[i],gg[i-1],sizeof gg[i]); gg[i][l[i]][r[i]]++; } while (q--) { Ans=0; // read(w); read(L); read(R); scanf("%s%d%d",w+1,&L,&R); for (int i=1;i<=k;i++) { now=1; for (int j=i;j<=k;j++) { x=w[j]-'a'; if (!Sam.T[now].ch[x]) break; now=Sam.T[now].ch[x]; Ans+=1ll*gg[R][i][j]*Sam.T[now].val; if (L) Ans-=1ll*gg[L-1][i][j]*Sam.T[now].val; } } printf("%lld\n",Ans); } } } } namespace work2 { int x,Len; vector<int> v[N]; void solve() { Sam.build(); while (q--) { Ans=0; scanf("%s%d%d",w+1,&L,&R); for (int i=L;i<=R;i++) v[r[i]].push_back(r[i]-l[i]+1); now=1;Len=0; for (int i=1;i<=k;i++) { x=w[i]-'a'; if (Sam.T[now].ch[x]) now=Sam.T[now].ch[x],Len++;else { while (now&&!Sam.T[now].ch[x]) now=Sam.T[now].fa; Len=Sam.T[now].len; if (!now) now=1,Len=0;else now=Sam.T[now].ch[x],Len++; } // cerr<<Len<<endl; for (auto j:v[i]) if (Len>=j) Ans+=Sam.que(now,j); v[i].clear(); } printf("%lld\n",Ans); } } } signed main () { scanf("%d%d%d%d",&n,&m,&q,&k); scanf("%s",w+1);len=strlen(w+1); for (int i=1;i<=len;i++) Sam.add(w[i]-'a'); for (int i=1;i<=Sam.tot;i++) c[Sam.T[i].len]++; for (int i=1;i<=len;i++) c[i]+=c[i-1]; for (int i=1;i<=Sam.tot;i++) id[c[Sam.T[i].len]--]=i; for (int i=Sam.tot;i;i--) Sam.T[Sam.T[id[i]].fa].val+=Sam.T[id[i]].val; for (int i=0;i<m;i++) scanf("%d%d",l+i,r+i),l[i]++,r[i]++; if (k<555) work1::solve(); else work2::solve(); return 0; }