[NOI2018]你的名字(SAM+线段树合并)

考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献。

T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长公共子串的长度。

从1到length(T)扫一遍,SAM基本操作求出每个前缀与S的最长公共子串。

答案为$sum_{i=1}^{cnt}max(0,mx[x]-max(mx[fa[x]],len[tag[x]]))$,其中tag[x]是x所代表的子串在T中的第一个出现位置,len[i]为T的前缀i与S的最长公共子串。

再考虑l,r任意的情况,唯一区别在于求某前缀与S的最长公共子串时,需要满足[l,r]的限制。

即若想判断当前公共子串长是否可以为len,那么当前走到的点的parent树的子树内必须存在一个串的Right在[l+len,r]中。

“某子树内是否存在[l+len,r]中的值”显然可以用主席树做,或可持久化线段树合并。

下面大概证一下线段树合并的时空复杂度:

1.由于每次合并时,若发现x与y的当前子树有一个为空则退出。这种情况每个节点最多会出现一次(合并之后这个位置就不再为空了),故这部分复杂度为$O(nlog n)$。

2.除去情况一,则x与y合并的复杂度为两个树的重合节点个数。最坏情况发生在依次将一个个只有一个节点的树合并进来,这部分显然仍然是$O(nlog n)$的。

3.不可持久化的线段树合并并不产生新节点,故空间复杂度为初始插入n个点的复杂度:$O(nlog n)$

4.可持久化的线段树,根据算法流程容易发现时空复杂度同阶,于是复杂度也为$O(nlog n)$

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define lson ls[x],L,mid
 5 #define rson rs[x],mid+1,R
 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 7 typedef long long ll;
 8 using namespace std;
 9 
10 const int N=2000010,M=40000010;
11 char s[N],t[N];
12 int n,m,nd,Q,l,r,len[N],rt[N],id[N],ls[M],rs[M];
13 
14 struct SAM{
15     int nd,lst,mx[N],fa[N],pos[N],son[N][27];
16     
17     void init(){
18         nd=1; lst=1;
19         rep(i,0,m*2) fa[i]=mx[i]=0;
20         rep(i,0,m*2) rep(j,0,26) son[i][j]=0;
21     }
22     
23     void ext(int c,int x){
24         int p=lst,np=lst=++nd; pos[np]=x; mx[np]=mx[p]+1;
25         while (p && !son[p][c]) son[p][c]=np,p=fa[p];
26         if (!p) fa[np]=1;
27         else{
28             int q=son[p][c];
29             if (mx[q]==mx[p]+1) fa[np]=q;
30             else{
31                 int nq=++nd; pos[nq]=pos[q]; mx[nq]=mx[p]+1;
32                 memcpy(son[nq],son[q],sizeof(son[q]));
33                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
34                 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
35             }
36         }
37     }
38     
39 }S,T;
40 
41 bool cmp(int a,int b){ return S.mx[a]<S.mx[b]; }
42 
43 void ins(int &x,int L,int R,int k){
44     if (!x) x=++nd;
45     if (L==R) return;
46     int mid=(L+R)>>1;
47     if (k<=mid) ins(lson,k); else ins(rson,k);
48 }
49 
50 bool que(int x,int L,int R,int l,int r){
51     if (!x) return 0;
52     if (L==l && r==R) return 1;
53     int mid=(L+R)>>1;
54     if (r<=mid) return que(lson,l,r);
55     else if (l>mid) return que(rson,l,r);
56         else return que(lson,l,mid)|que(rson,mid+1,r);
57 }
58 
59 int merge(int x,int y){
60     if (!x || !y) return x|y;
61     int now=++nd;
62     ls[now]=merge(ls[x],ls[y]);
63     rs[now]=merge(rs[x],rs[y]);
64     return now;
65 }
66 
67 int main(){
68     freopen("name.in","r",stdin);
69     freopen("name.out","w",stdout);
70     scanf("%s",s+1); n=strlen(s+1);
71     S.init(); rep(i,1,n) S.ext(s[i]-'a',i),ins(rt[S.lst],1,n,i);
72     rep(i,1,S.nd) id[i]=i;
73     sort(id+1,id+S.nd+1,cmp);
74     for (int i=S.nd; i>1; i--) rt[S.fa[id[i]]]=merge(rt[S.fa[id[i]]],rt[id[i]]);
75     for (scanf("%d",&Q); Q--; ){
76         scanf("%s%d%d",t+1,&l,&r); m=strlen(t+1);
77         int now=0,x=1;
78         T.init(); rep(i,1,m) T.ext(t[i]-'a',i);
79         rep(i,1,m){
80             int c=t[i]-'a';
81             while (1){
82                 if (!que(rt[S.son[x][c]],1,n,l+now,r)){
83                     if (!now) break; now--;
84                     if (now==S.mx[S.fa[x]]) x=S.fa[x];
85                 }else{ now++; x=S.son[x][c]; break; }
86             }
87             len[i]=now;
88         }
89         ll ans=0;
90         rep(i,2,T.nd) ans+=max(0,T.mx[i]-max(T.mx[T.fa[i]],len[T.pos[i]]));
91         printf("%lld
",ans);
92     }
93     return 0;
94 }
原文地址:https://www.cnblogs.com/HocRiser/p/10229780.html