标签:oid 重复 位置 nbsp gis amp col space etc
求不同的子串个数
如果规定根的话,那么弯曲的路径难以处理。
由于只有20个叶子,所以以每个叶子分别为根,建20棵trie树,再把20棵trie树合成一棵。
这样,trie上一个到某个祖先的路径构成了所有的子串。(可能重复)
所以trie上建SAM。SAM的路径条数(或者每个点的len[i]-len[fa[i]])就是本质不同的子串个数
至于trie上建SAM,可以dfs,或者bfs建。
只要记录树上x的father的SAM上的节点编号p,从这个p作为nd开始建即可。
#include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^‘0‘) using namespace std; typedef long long ll; il void rd(int &x){ char ch;bool fl=false; while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=100000+5; int n,m; int clo[N]; int du[N]; struct node{ int nxt,to; }e[2*N]; int hd[N],cnt; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } struct trie{ int cnt,ch[20*N][10]; trie(){ cnt=1; } }tr; void dfs(int x,int y,int fa){ if(!tr.ch[y][clo[x]]){ tr.ch[y][clo[x]]=++tr.cnt; } y=tr.ch[y][clo[x]]; for(reg i=hd[x];i;i=e[i].nxt){ int z=e[i].to; if(z==fa) continue; dfs(z,y,x); } } struct SAM{ int fa[20*N],len[20*N],ch[20*N][10],cnt; ll s[20*N]; SAM(){ cnt=1; } int ins(int c,int p){ //cout<<" c p "<<c<<" "<<p<<endl; int np=++cnt;len[np]=len[p]+1; while(p&&ch[p][c]==0) ch[p][c]=np,p=fa[p]; if(!p){ fa[np]=1;return np; } int q=ch[p][c]; if(len[q]==len[p]+1){ fa[np]=q;return np; } len[++cnt]=len[p]+1; fa[cnt]=fa[q];fa[q]=fa[np]=cnt; for(reg i=0;i<m;++i) ch[cnt][i]=ch[q][i]; while(p&&ch[p][c]==q) ch[p][c]=cnt,p=fa[p]; return np; } ll dfs(int x){ if(s[x]) return s[x]; s[x]=1; for(reg i=0;i<m;++i){ if(ch[x][i]) { s[x]+=dfs(ch[x][i]); } } return s[x]; } }sam; int pos[20*N]; void build(int x){ for(reg i=0;i<m;++i){ if(tr.ch[x][i]){ pos[tr.ch[x][i]]=sam.ins(i,pos[x]); build(tr.ch[x][i]); } } } int main(){ rd(n);rd(m); for(reg i=1;i<=n;++i) rd(clo[i]); int x,y; for(reg i=1;i<=n-1;++i){ rd(x);rd(y); add(x,y);add(y,x); ++du[x];++du[y]; } for(reg i=1;i<=n;++i){ if(du[i]==1){ //cout<<" st "<<i<<endl; dfs(i,1,0); } } //cout<<" trie‘s cnt "<<tr.cnt<<endl; pos[1]=1; build(1); printf("%lld\n",sam.dfs(1)-1); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/12/12 11:22:24 */
其实不用建出合并的trie。
直接在20次dfs的时候(每次dfs都是一个小trie),20次每次从根开始插入即可。
重复的前缀部分会和根节点隔离开的。既不会能从根节点到达,而且fa[i]=len[i],没有包含一个子串。
#include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^‘0‘) using namespace std; typedef long long ll; il void rd(int &x){ char ch;bool fl=false; while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=100010; #define TOT 2000000 struct node{ int nxt,to; }e[2*N]; int hd[N],cnt; int n,m; int clo[N]; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } struct SAM{ int fa[TOT],len[TOT],ch[TOT][10],cnt; ll s[TOT]; SAM(){ cnt=1; } int ins(int c,int p){ //cout<<" c p "<<c<<" "<<p<<endl; int np=++cnt;len[np]=len[p]+1; while(p&&ch[p][c]==0) ch[p][c]=np,p=fa[p]; if(!p){ fa[np]=1;return np; } int q=ch[p][c]; if(len[q]==len[p]+1){ fa[np]=q;return np; } len[++cnt]=len[p]+1; fa[cnt]=fa[q];fa[q]=fa[np]=cnt; for(reg i=0;i<m;++i) ch[cnt][i]=ch[q][i]; while(p&&ch[p][c]==q) ch[p][c]=cnt,p=fa[p]; return np; } ll dfs(int x){ if(s[x]) return s[x]; s[x]=1; for(reg i=0;i<m;++i){ if(ch[x][i]) { s[x]+=dfs(ch[x][i]); } } return s[x]; } }sam; int du[N]; void dfs(int x,int fa,int p){ p=sam.ins(clo[x],p); for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa) continue; dfs(y,x,p); } } int main(){ rd(n);rd(m); for(reg i=1;i<=n;++i) rd(clo[i]); int x,y; for(reg i=1;i<=n-1;++i){ rd(x);rd(y); add(x,y);add(y,x); du[x]++;du[y]++; } for(reg i=1;i<=n;++i){ if(du[i]==1) dfs(i,0,1); } //cout<<sam.cnt<<endl; printf("%lld",sam.dfs(1)-1); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/12/12 15:07:01 */
(当然,如果广义SAM要处理Right集合的大小的话,要根据建造的方法处理
1.所有的串依次从rt=1开始插入
叶子sz赋值为1,然后Parent树上往上push。注意,不管节点是否有实际意义,都要push上去。因为代表一个出现的位置
2.trie树
先要找到trie树某个点的sz[x],然后插入的时候sz就是这个sz[x]
)
标签:oid 重复 位置 nbsp gis amp col space etc
原文地址:https://www.cnblogs.com/Miracevin/p/10109139.html