[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(n\log n)$。
2.除去情况一,则x与y合并的复杂度为两个树的重合节点个数。最坏情况发生在依次将一个个只有一个节点的树合并进来,这部分显然仍然是$O(n\log n)$的。
3.不可持久化的线段树合并并不产生新节点,故空间复杂度为初始插入n个点的复杂度:$O(n\log n)$
4.可持久化的线段树,根据算法流程容易发现时空复杂度同阶,于是复杂度也为$O(n\log 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\n",ans); 92 } 93 return 0; 94 }