[后缀自动机 阈值] LOJ#6031. 「雅礼集训 2017 Day1」字符串

k 阈值
如果 kS
枚举询问串的每个子串,在后缀自动机上找到对应的节点就可以了

O(qkk2logn)

如果 k>S
枚举询问串的前缀,找到对应节点,每个和这个前缀相关的询问是这个前缀的后缀,在fail树上倍增

O(qk(k+mlogn))

这个log我卡不掉,只能对着数据卡常了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstring>
#include <map>

using namespace std;

typedef long long ll;

const int N=200010;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void read(char *a){
  char c=nc(); int x=0;
  for(;c>'z'||c<'a';c=nc());for(;c>='a'&&c<='z';a[++x]=c,c=nc());
}

int n,m,q,k,S,l[N],r[N];
char s[N];

int p=1,cnt=1,nxt[N][30],fail[N],len[N],val[N];

inline void extend(int c){
  int np=++cnt; len[np]=len[p]+1; val[np]=1;
  while(p && !nxt[p][c]) nxt[p][c]=np,p=fail[p];
  if(!p) fail[np]=1;
  else{
    int q=nxt[p][c];
    if(len[q]==len[p]+1) fail[np]=q;
    else{
      int nq=++cnt; len[nq]=len[p]+1;
      memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
      fail[nq]=fail[q];
      fail[q]=fail[np]=nq;
      while(p && nxt[p][c]==q) nxt[p][c]=nq,p=fail[p];
    }
  }
  p=np;
}

int id[N],t[N],fa[N][20];

inline void Pre(){
  for(int i=1;i<=cnt;i++) t[len[i]]++;
  for(int i=1;i<=cnt;i++) t[i]+=t[i-1];
  for(int i=1;i<=cnt;i++) id[t[len[i]]--]=i;
  for(int i=cnt;i;i--)
    val[fail[id[i]]]+=val[id[i]];
  for(int i=1;i<=cnt;i++){
    int x=id[i]; fa[x][0]=fail[x];
    for(int j=1;j<=10;j++) fa[x][j]=fa[fa[x][j-1]][j-1];
  }
}

const int MAX=316*320+320;

int ans[N];
vector<int> M[MAX];
int ap[MAX],flg[MAX],pr[320][N>>1];

inline int calc(pair<int,int> A,int L,int R){
  int g=A.first*320+A.second;
  if(flg[g]){
    int cur=flg[g];
    return pr[cur][R]-pr[cur][L-1];
  }
  vector<int> c=M[A.first*320+A.second];
  return upper_bound(c.begin(),c.end(),R)-lower_bound(c.begin(),c.end(),L);
}

void PutAns(ll x){
  if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

inline void Case1(){
  for(int i=1;i<=m;i++) ap[l[i]*320+r[i]]++;
  int S=316,cc=0;
  for(int l=1;l<=k;l++)
    for(int r=l;r<=k;r++)
      if(ap[l*320+r]>S){
    flg[l*320+r]=++cc;
    for(int i=1;i<=m;i++)
      pr[cc][i]=pr[cc][i-1]+(::l[i]==l && ::r[i]==r);
      }
  for(int i=1;i<=m;i++) M[l[i]*320+r[i]].push_back(i);
  while(q--){
    int L,R;
    read(s); read(L); read(R);
    L++; R++;
    ll ans=0;
    for(int l=1;l<=k;l++){
      int cur=1;
      for(int r=l;r<=k;r++){
    if(!nxt[cur][s[r]-'a']) break;
    cur=nxt[cur][s[r]-'a'];
    ans+=val[cur]*calc(make_pair(l,r),L,R);
      }
    }
    PutAns(ans); putchar('\n');
  }
}

vector<pair<int,int> > Q[N];

inline void Case2(){
  for(int i=1;i<=m;i++) Q[r[i]].push_back(make_pair(l[i],i));
  while(q--){
    read(s);
    int L,R; read(L); read(R);
    L++; R++;
    int cur=1,cnt=0; ll ans=0;
    for(int r=1;r<=k;r++){
      while(cur && !nxt[cur][s[r]-'a'])
    cur=fail[cur],cnt=len[cur];
      if(!cur){
    cur=1; cnt=0; continue;
      }
      cur=nxt[cur][s[r]-'a']; cnt++;
      for(auto u : Q[r]){
    int l=u.first,t=u.second;
    if(r-l+1>cnt || t<L || t>R) continue;
    int c=cur;
    for(int i=10;~i;i--)
      if(len[fa[c][i]]>=r-l+1) c=fa[c][i];
    ans+=val[c];
      }
    }
    PutAns(ans); putchar('\n');
  }
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(n); read(m); read(q); read(k); 
  read(s);
  for(int i=1;i<=n;i++) extend(s[i]-'a');
  Pre();
  for(int i=1;i<=m;i++)
    read(l[i]),read(r[i]),l[i]++,r[i]++;
  k<=310?Case1():Case2();
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值