NOI2018 你的名字。
您的名字。
做法好像和其他题解不太一样。
空间少个 \(\log\),而且常数小。
description
给定长度为 \(n\) 的字符串 \(S\)。
\(Q\) 次询问,第 \(t\) 次给定字符串 \(T_t\) 以及正整数 \(l,r\in [1,n]\)。
每次询问回答 \(T_t\) 有几个本质不同子串在 \(S_{l\dots r}\) 中没有出现过。
不强制在线
- 
\(n\leq 5\times 10^5\) 
- 
\(Q\leq 10^5\) 
- 
\(|T_t|\leq 5\times 10^5,\sum |T_t|\leq 10^6\) 
solution
按照 \(r\) 不降离线。
对于第 \(t\) 个询问,先计算出来 \(w_t\) 表示字符串 \(T_t\) 的本质不同子串个数。
然后要容斥掉在 \(S_{l\dots r}\) 中出现过的。
通过在 SAM 上跳 parent 匹配出 \(T_t\) 的每个前缀 \(i\) 的后缀和 \(S\) 的最长匹配长度 \(Len_i\),并求出匹配到了 \(S\) 的 SAM 上的点 \(p_i\)。
再计算出 \(T_t\) 的每个前缀 \(i\) 有哪些长度的后缀是在 \(T_t\) 中第一次出现,容易发现,这些后缀的长度是连续的,构成区间 \([\text{minl}_i,\text{maxl}_i]\),这一步可以在 \(T_t\) 的 SAM 的 parent 树上 dfs 实现。
然后我们需要知道前缀 \(i\) 和 \(S_{l\dots r}\) 的最长匹配长度,和 \([\text{minl}_i,\text{maxl}_i]\) 取交就是对答案的贡献。
我们已经求出了前缀 \(i\) 匹配到的点 \(p\),那么能只有 \(p\) 和其 parent 树上的祖先可以和前缀 \(i\) 匹配。因为我们离线了,所以不需要考虑右端点的限制。设 \(S\) 的 SAM 上点 \(x\) 当前的 \(\text{endpos}\) 集合中最大的元素是 \(\text{maxr}(x)\),\(x\) 表示的字符串中最长的长度记作 \(\text{len}(x)\)。那么 \(x\) 代表的一些字符串和前缀 \(i\) 的最长匹配长度就是 \(\min(\text{maxr}(x)-l+1,\text{len}(x))\)。
容易发现,点 \(p\) 向上走的过程中,\(\text{len}\) 是递减,\(\text{maxr}\) 是不降的,所以可以二分 \(\text{maxr}-\text{len}+1\) 和 \(l\) 的大小,最长长度一定在临界处两个点取到。
具体实现可以用 LCT,每次离线的右指针右移就 access,然后在 Splay 上打标记,二分也可以在 Splay 上完成。注意,这时求出来的最长长度可能包含了 \(p\) 点原本不能匹配的字符串(即 \(Len_i<\text{len}(p)\),但是实际有可能取到比 \(Len_i\) 大),所以求出来的长度要和 \(Len_i\) 取 \(\min\)。
时间复杂度 \(O(n\log n+\sum |T|\log |T|)\),空间复杂度 \(O((n+\sum |T|)|\Sigma|)\)。
顺便提一下,这题原数据似乎有一部分比较水,Splay 上二分的方向写反得了 54 分 qwq
code
#include<bits/stdc++.h>
using namespace std;
using E=long long;
constexpr int inf=0x3f3f3f3f;
struct SAM{
  vector<array<int,26>> ch;
  vector<int> fa,len;
  int idx,lst;
  int extend(int c){
    idx++;
    len[idx]=len[lst]+1;
    int p=lst;
    while(p&&ch[p][c]==0){
      ch[p][c]=idx;
      p=fa[p];
    }
    if(!p) return fa[idx]=1,lst=idx,idx;
    int q=ch[p][c];
    if(len[q]==len[p]+1) return fa[idx]=q,lst=idx,idx;
    int np=idx;
    idx++;
    fa[idx]=fa[q],ch[idx]=ch[q],len[idx]=len[p]+1;
    while(p&&ch[p][c]==q){
      ch[p][c]=idx;
      p=fa[p];
    }
    return fa[q]=fa[np]=idx,lst=np,np;
  }
  SAM(int n){
    ch.resize(n*2+1);
    fa.resize(n*2+1),len.resize(n*2+1);
    lst=idx=1;
  }
};
struct linkCutTree{
  struct node{
    int son[2],par,tag,w,val;
    node(){
      son[0]=son[1]=par=tag=w=val=0;
    }
  };
  vector<node> tr;
  linkCutTree(int sz){
    tr=vector<node>(sz+1);
  }
  #define ls(u) tr[(u)].son[0]
  #define rs(u) tr[(u)].son[1]
  #define par(u) tr[(u)].par
  #define w(u) tr[(u)].w
  #define tag(u) tr[(u)].tag
  #define val(u) tr[(u)].val
  void spread(int u,int v){
    if(!u) return;
    w(u)=v-val(u)+1;
    tag(u)=v;
  }
  void dn(int u){
    if(tag(u)) spread(ls(u),tag(u)),spread(rs(u),tag(u)),tag(u)=0;
  }
  int get(int u){ return u==rs(par(u)); }
  int nroot(int u){ return u==ls(par(u))||get(u); }
  void rotate(int u){
    int p=par(u),j=par(p),op=get(u);
    if(nroot(p)) tr[j].son[get(p)]=u;
    if(tr[u].son[op^1]) par(tr[u].son[op^1])=p;
    tr[p].son[op]=tr[u].son[op^1];
    tr[u].son[op^1]=p;
    par(p)=u;
    par(u)=j;
  }
  void update(int u){
    if(nroot(u)) update(par(u));
    dn(u);
  }
  void splay(int u){
    update(u);
    while(nroot(u)){
      int f=par(u);
      if(nroot(f)){
        if(get(u)==get(f)) rotate(f);
        else rotate(u);
      }
      rotate(u);
    }
  }
  void access(int u){
    int p=0;
    while(u){
      splay(u);
      rs(u)=p;
      p=u;
      u=par(u);
    }
  }
  void reset(int u,int v,int fa){
    par(u)=fa,val(u)=v; w(u)=-v+1;
  }
  #undef ls
  #undef rs
  #undef tag
  #undef par
  #undef w
  #undef val
};
int main(){
#ifdef zzafanti
  freopen("in.in","r",stdin);
#endif // zzafanti
  cin.tie(nullptr),cout.tie(nullptr)->sync_with_stdio(false);
  string Str;
  cin>>Str;
  int n=Str.size();
  Str=" "+Str;
  SAM sam1(n);
  vector<int> pos(n+1);
  int Q;
  cin>>Q;
  vector<pair<int,int>> querys(Q+1);
  vector<E> ans(Q+1);
  vector<vector<pair<int,string>>> Tp(n+1);
  for(int t=1; t<=Q; t++){
    string T;
    cin>>T;
    int l,r;
    cin>>l>>r;
    Tp[r].emplace_back(make_pair(t,T));
    querys[t]=make_pair(l,r);
  }
  for(int i=1; i<=n; i++){
    pos[i]=sam1.extend(Str[i]-'a');
  }
  linkCutTree S(sam1.idx);
  for(int i=1; i<=sam1.idx; i++){
    S.reset(i,sam1.len[i],sam1.fa[i]);
  }
  for(int R=1; R<=n; R++){
    S.access(pos[R]);
    S.splay(1);
    S.spread(1,R);
    for(auto pt:Tp[R]){
      int l=querys[pt.first].first,r=querys[pt.first].second;
      string &T=pt.second;
      int t=pt.first;
      int m=T.size();
      SAM sam(m+1);
      vector<int> maxr(m*2+1);
      for(int i=1; i<=m; i++){
        maxr[sam.extend(T[i-1]-'a')]=i;
      }
      vector<vector<int>> ver(m*2+1);
      for(int i=2; i<=sam.idx; i++){
        ans[t]+=(sam.len[i]-sam.len[sam.fa[i]]);
        ver[sam.fa[i]].emplace_back(i);
      }
      vector<int> maxl(m+1,0),minl(m+1,inf);
      function<void(int)> dfs=[&](int u)->void{
        for(auto p:ver[u]){
          dfs(p);
          maxr[u]=max(maxr[u],maxr[p]);
        }
        maxl[maxr[u]]=max(maxl[maxr[u]],sam.len[u]);
        minl[maxr[u]]=min(minl[maxr[u]],sam.len[sam.fa[u]]+1);
        assert(minl[maxr[u]]>0);
      };
      dfs(1);
      int now=0,p=1;
      for(int i=1; i<=m; i++){
        int c=T[i-1]-'a';
        while(p!=1&&sam1.ch[p][c]==0) p=sam1.fa[p],now=sam1.len[p];
        if(sam1.ch[p][c]) p=sam1.ch[p][c],now++;
        int res=0;
        S.access(p);
        S.splay(p);
        //cerr<<S.tr[p].val<<endl;
        int tmp=p,lst=0,llst=0;
        while(tmp){
          llst=tmp;
          //cerr<<tmp<<' '<<S.tr[tmp].w<<' '<<l<<endl;
          if(S.tr[tmp].w<=l) lst=tmp,tmp=S.tr[tmp].son[0];
          else tmp=S.tr[tmp].son[1];
          if(tmp) S.dn(tmp);
        }
        tmp=lst;
        if(tmp){
          S.splay(tmp);
          //cerr<<S.tr[tmp].w<<' '<<S.tr[tmp].val<<endl;
          res=(S.tr[tmp].w+S.tr[tmp].val-1)-l+1;
          res=max(0,res);
          tmp=S.tr[tmp].son[0];
        }else res=S.tr[p].val,S.splay(llst);
        if(tmp){
          while(S.tr[tmp].son[1]) tmp=S.tr[tmp].son[1],S.dn(tmp);
          S.splay(tmp);
          //cerr<<tmp<<' '<<S.tr[tmp].val<<' '<<S.tr[tmp].w<<endl;
          res=max(res,S.tr[tmp].val);
        }
        res=min(res,now);
        ans[t]-=max(0,min(maxl[i],res)-minl[i]+1);
      }
    }
  }
  for(int i=1; i<=Q; i++) cout<<ans[i]<<'\n';
  return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号