LOJ #6031. 「雅礼集训 2017 Day1」字符串

\(SAM\) + 分类讨论

先建出 \(SAM\) 然后求出 \(|{\rm endpos}(p)|\)

$ \sum w=kq\leq 10^5$,于是对 \(k\) 的大小分类讨论。

\(k\geq\sqrt n\)\(q\leq\sqrt n\) ,直接在 \(SAM\) 匹配 \(w\) ,求出每个位置在 \(SAM\) 上的点。对于每个询问,倍增向上跳到最靠上的且 \({\rm len}(p) \geq qr-ql+1\) 的点 ,答案加上 \(|{\rm endpos}(p)|\)

这时 q\leqslant\sqrt{n}qn ,我们直接在 SAMSAM 暴力匹配 ww ,找出所有 ww 的前缀对应的节点,之后枚举询问,倍增跳到 w[l_i...r_i]w[l**i...r**i] 这个节点,将答案加上该节点的 endposendpo**s 集合大小即可。

\(k< \sqrt n\)\(q>\sqrt n\) ,这时考虑 \(qK^2\) 的算法,我们可以开一个桶 \(v[ql][qr]\) 记录所有询问,并枚举 \(w\) 的所有前缀进行匹配,二分出它在 \([a,b]\) 这段询问区间中出现的次数,乘上匹配长度加到答案中。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
  register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
  do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=200010,B=17,D=350;
int n,m,Q,K,tot=1,lst=1;
int LL[N],RR[N],f[N][B+1];
int fa[N],c[N][26],len[N],d[N],sz[N],mem[N];
char s[N];
inline void add(int ch) {
  R p=lst,np=lst=++tot;
  len[np]=len[p]+1,++sz[tot];
  while(p&&!c[p][ch]) c[p][ch]=np,p=fa[p];
  if(!p) return fa[np]=1,void();
  R q=c[p][ch];
  if(len[q]==len[p]+1) return fa[np]=q,void();
  else {
    R nq=++tot;
    memcpy(c[nq],c[q],26<<2);
    fa[nq]=fa[q],len[nq]=len[p]+1;
    fa[np]=fa[q]=nq;
    while(p&&c[p][ch]==q) c[p][ch]=nq,p=fa[p]; 
  }
}
int mp[N],ml[N];
inline void solve1() {
  register ll ans;
  for(R i=1;i<=m;++i) LL[i]=g()+1,RR[i]=g()+1;
  while(Q--) {
    scanf("%s",s+1);
    R ql=g()+1,qr=g()+1;
    R p=1,l=0;
    for(R i=1;i<=K;++i) {
      R ch=s[i]-'a';
      while(p&&!c[p][ch]) p=fa[p],l=len[p];
      if(!p) {p=1,l=0,mp[i]=0; continue;}
      p=c[p][ch],++l,mp[i]=p,ml[i]=l;
    } ans=0;
    for(R i=ql,p,l,lim;i<=qr;++i) {
      p=mp[RR[i]],l=ml[RR[i]],lim=RR[i]-LL[i]+1;
      if(!p||l<lim) continue;
      for(R t=B;~t;--t) if(f[p][t]&&len[f[p][t]]>=lim)
        p=f[p][t];
      ans+=sz[p];
    } printf("%lld\n",ans);
  }
}
vector<int> q[D][D];
inline void solve2() {
  register ll ans=0;
  for(R i=1,l,r;i<=m;++i) 
    l=g()+1,r=g()+1,q[l][r].push_back(i);
  while(Q--) {
    ans=0;
    scanf("%s",s+1);
    R ql=g()+1,qr=g()+1;
    for(R i=1;i<=K;++i) for(R j=i,p=1;j<=K;++j) {
      p=c[p][s[j]-'a']; if(!p) break;
      ans+=1ll*(upper_bound(q[i][j].begin(),q[i][j].end(),qr)-
                lower_bound(q[i][j].begin(),q[i][j].end(),ql))*sz[p];
    } printf("%lld\n",ans);
  }  
}
inline void main() {
  n=g(),m=g(),Q=g(),K=g();
  scanf("%s",s+1);
  for(R i=1;i<=n;++i) add(s[i]-'a');
  for(R i=1;i<=tot;++i) ++d[len[i]];
  for(R i=1;i<=n;++i) d[i]+=d[i-1];
  for(R i=1;i<=tot;++i) mem[d[len[i]]--]=i;
  for(R i=tot;i;--i) sz[fa[mem[i]]]+=sz[mem[i]];
  for(R i=1;i<=tot;++i) f[i][0]=fa[i];
  for(R t=1;t<=B;++t) for(R i=1;i<=tot;++i)
    f[i][t]=f[f[i][t-1]][t-1];
  if(K>=sqrt(n)) solve1();
  else solve2();
}
} signed main() {Luitaryi::main(); return 0;}

2020.01.10

posted @ 2020-01-10 18:23  LuitaryiJack  阅读(103)  评论(0编辑  收藏  举报