标签:isp bsp cto 线段树 merge eve push 注意 倍增
题意:给定字符串S,然后M个字符串T。Q次询问,每次给出(L,R,l,r),问S[l,r]在L到R这些T字符串中,在哪个串出现最多,以及次数。
思路:把所有串建立SAM,然后可以通过倍增走到[l,r]在SAM上的位置p,然后在这个位置p上求,求的过程就是一个线段树求区间最值。 现在的关键是得到线段树,这个线段树记录了endpos,这个可以用启发式合并。
注意现在是广义后缀自动机,不能用拓扑排序合并线段树。 必须用fail树DFS来合并。 不然会出错。 具体我也不知道,不过我估计是因为广义自动机里面有空串导致的。
#include<bits/stdc++.h>
#define rep2(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1100010;
char c[maxn],t[maxn];
int rt[maxn],pos[maxn],fcy[maxn],N,M,Q,tot;
int f[maxn][20],ans,num;
struct in{
int L,R,Mx,id;
}s[maxn*20];
void update(int now)
{
if(s[s[now].L].Mx>=s[s[now].R].Mx) {
s[now].Mx=s[s[now].L].Mx;
s[now].id=s[s[now].L].id;
}
else {
s[now].Mx=s[s[now].R].Mx;
s[now].id=s[s[now].R].id;
}
}
void ins(int &now,int L,int R,int p)
{
if(!now) now=++tot;
if(L==R){ s[now].Mx++; s[now].id=L; return ;}
int Mid=(L+R)>>1;
if(p<=Mid) ins(s[now].L,L,Mid,p);
else ins(s[now].R,Mid+1,R,p);
update(now);
}
void merge(int &now,int x,int y,int L,int R)
{
if(!x||!y) { now= x|y; return ;}
now=++tot;int Mid=(L+R)>>1;
if(L==R) { s[now].Mx=s[x].Mx+s[y].Mx; s[now].id=L; return ;}
merge(s[now].L,s[x].L,s[y].L,L,Mid);
merge(s[now].R,s[x].R,s[y].R,Mid+1,R);
update(now);
}
void query(int Now,int L,int R,int l,int r)
{
if(!Now||l>r) return ;
if(s[Now].Mx<num) return ;
if(l<=L&&r>=R) {
if(s[Now].Mx>num) num=s[Now].Mx,ans=s[Now].id;
else if(s[Now].Mx==num&&num!=0) ans=min(ans,s[Now].id);
return ;
}
int Mid=(L+R)>>1;
if(l<=Mid) query(s[Now].L,L,Mid,l,r);
if(r>Mid) query(s[Now].R,Mid+1,R,l,r);
}
struct SAM{
int ch[maxn][26],fa[maxn],maxlen[maxn],cnt,last;
int a[maxn],b[maxn];
void init()
{
cnt=last=1;
memset(ch[1],0,sizeof(ch[1]));
}
void add(int x,int id)
{
int np=++cnt,p=last; last=np;
if(id>0) ins(rt[np],1,M,id);
maxlen[np]=maxlen[p]+1; memset(ch[np],0,sizeof(ch[np]));
while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
if(!p) fa[np]=1;
else {
int q=ch[p][x];
if(maxlen[q]==maxlen[p]+1) fa[np]=q;
else {
int nq=++cnt; maxlen[nq]=maxlen[p]+1;
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
}
}
}
vector<int>G[maxn];
void dfs(int u)
{
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
f[v][0]=u; dfs(v);
merge(rt[u],rt[u],rt[v],1,M);
}
}
void DFS()
{
rep(i,2,cnt) G[fa[i]].push_back(i);
dfs(1);
}
}T;
void solve()
{
int L,R,l,r,len;
scanf("%d%d%d%d",&L,&R,&l,&r);
len=r-l+1;
int now=pos[r];
if(T.maxlen[now]<len) {
printf("%d %d\n",L,0);
return ;
}
for(int i=19;i>=0;i--)
if(T.maxlen[f[now][i]]>=len) now=f[now][i];
ans=L; num=0;
query(rt[now],1,M,L,R);
printf("%d %d\n",ans,num);
}
int main()
{
T.init();
scanf("%s",t+1);
N=strlen(t+1); T.last=1;
rep(i,1,N) T.add(t[i]-‘a‘,0),pos[i]=T.last;
scanf("%d",&M);
rep(i,1,M) {
T.last=1;
scanf("%s",c+1); N=strlen(c+1);
rep(j,1,N) T.add(c[j]-‘a‘,i);
}
T.DFS();
rep(i,1,19) rep(j,2,T.cnt) f[j][i]=f[f[j][i-1]][i-1];
scanf("%d",&Q);
while(Q--) solve();
return 0;
}
稍微优化一下,为了省空间和时间,也可以手动插入S,即S没有必要插入SAM中。
#include<bits/stdc++.h> #define rep2(i,a,b) for(int i=a;i>=b;i--) #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=510010; char c[maxn],t[maxn]; int rt[maxn],pos[maxn],fcy[maxn],N,M,Q,tot; int f[maxn][20],ans,num; struct in{ int L,R,Mx,id; }s[maxn*40]; void update(int now) { if(s[s[now].L].Mx>=s[s[now].R].Mx) { s[now].Mx=s[s[now].L].Mx; s[now].id=s[s[now].L].id; } else { s[now].Mx=s[s[now].R].Mx; s[now].id=s[s[now].R].id; } } void ins(int &now,int L,int R,int p) { if(!now) now=++tot; if(L==R){ s[now].Mx++; s[now].id=L; return ;} int Mid=(L+R)>>1; if(p<=Mid) ins(s[now].L,L,Mid,p); else ins(s[now].R,Mid+1,R,p); update(now); } void merge(int &now,int x,int y,int L,int R) { if(!x||!y) { now= x|y; return ;} now=++tot;int Mid=(L+R)>>1; if(L==R) { s[now].Mx=s[x].Mx+s[y].Mx; s[now].id=L; return ;} merge(s[now].L,s[x].L,s[y].L,L,Mid); merge(s[now].R,s[x].R,s[y].R,Mid+1,R); update(now); } void query(int Now,int L,int R,int l,int r) { if(!Now||l>r) return ; if(s[Now].Mx<num) return ; if(l<=L&&r>=R) { if(s[Now].Mx>num) num=s[Now].Mx,ans=s[Now].id; else if(s[Now].Mx==num&&num!=0) ans=min(ans,s[Now].id); return ; } int Mid=(L+R)>>1; if(l<=Mid) query(s[Now].L,L,Mid,l,r); if(r>Mid) query(s[Now].R,Mid+1,R,l,r); } struct SAM{ int ch[maxn][26],fa[maxn],maxlen[maxn],cnt,last; int a[maxn],b[maxn]; void init() { cnt=last=1; memset(ch[1],0,sizeof(ch[1])); } void add(int x,int id) { int np=++cnt,p=last; last=np; if(id>0) ins(rt[np],1,M,id); maxlen[np]=maxlen[p]+1; memset(ch[np],0,sizeof(ch[np])); while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=ch[p][x]; if(maxlen[q]==maxlen[p]+1) fa[np]=q; else { int nq=++cnt; maxlen[nq]=maxlen[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq; memcpy(ch[nq],ch[q],sizeof(ch[q])); while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p]; } } } vector<int>G[maxn]; void dfs(int u) { for(int i=0;i<G[u].size();i++){ int v=G[u][i]; f[v][0]=u; dfs(v); merge(rt[u],rt[u],rt[v],1,M); } } void DFS() { rep(i,2,cnt) G[fa[i]].push_back(i); dfs(1); } }T; void solve() { int L,R,l,r,len; scanf("%d%d%d%d",&L,&R,&l,&r); len=r-l+1; int now=pos[r]; if(fcy[r]<len) { printf("%d %d\n",L,0); return ; } for(int i=19;i>=0;i--) if(T.maxlen[f[now][i]]>=len) now=f[now][i]; ans=L; num=0; query(rt[now],1,M,L,R); printf("%d %d\n",ans,num); } int main() { T.init(); scanf("%s",t+1); scanf("%d",&M); rep(i,1,M) { T.last=1; scanf("%s",c+1); N=strlen(c+1); rep(j,1,N) T.add(c[j]-‘a‘,i); } int now=1,len=0; N=strlen(t+1); rep(i,1,N) { int x=t[i]-‘a‘; if(T.ch[now][x]) now=T.ch[now][x],len++; else { while(now&&!T.ch[now][x]) now=T.fa[now]; if(now==0) now=1,len=0; else len=T.maxlen[now]+1,now=T.ch[now][x]; } pos[i]=now; fcy[i]=len; } T.DFS(); rep(i,1,19) rep(j,2,T.cnt) f[j][i]=f[f[j][i-1]][i-1]; scanf("%d",&Q); while(Q--) solve(); return 0; }
CodeForces - 666E: Forensic Examination (SAM 线段树合并)
标签:isp bsp cto 线段树 merge eve push 注意 倍增
原文地址:https://www.cnblogs.com/hua-dong/p/11436485.html