LOJ#6198. 谢特 SAM+启发式合并+01trie
并不难的一道字符串题.
显然后缀自动机上进行字典树的启发式合并.
但是一定注意,题中要求的是两个后缀的 LCP 而不是两个前缀的 LCP.
所以在构建后缀自动机的时候要从后向前构建.
刚开始从前向后构建 WA 了半天.
然后进行启发式合并的时候可以对每个节点维护一个 id[x],如果儿子的大小大于点 $x$ 大小就 swap 一下即可.
code:
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> #define N 200008 #define ll long long #define pb push_back #define setIO(s) freopen(s".in","r",stdin) using namespace std; int ans; int last,tot,cnt,n,edges; char str[N]; vector<int>se[N]; int hd[N],to[N],nex[N]; int pre[N],mx[N],ch[N][27],id[N],rt[N],val[N]; struct data { int ch[2],si; }s[N*50]; void add(int u,int v) { nex[++edges]=hd[u]; hd[u]=edges,to[edges]=v; } void ins(int &x,int l,int v) { if(!x) { x=++cnt; } if(l==-1) { return; } int d=(v>>l)&1; ins(s[x].ch[d],l-1,v),++s[s[x].ch[d]].si; } int query(int x,int l,int v) { if(l==-1) { return 0; } int d=1^((v>>l)&1); if(s[x].ch[d]) { return (1<<l)+query(s[x].ch[d],l-1,v); } else { return query(s[x].ch[1^d],l-1,v); } } void init() { last=tot=1; for(int i=1;i<N;++i) id[i]=i; } void extend(int c,int v) { int np=++tot,p=last; mx[np]=mx[p]+1,last=np; for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np; if(!p) { pre[np]=1; } else { int q=ch[p][c]; if(mx[q]==mx[p]+1) pre[np]=q; else { int nq=++tot; mx[nq]=mx[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); pre[nq]=pre[q],pre[q]=pre[np]=nq; for(;p&&ch[p][c]==q;p=pre[p]) ch[p][c]=nq; } } se[np].pb(v); ins(rt[np],22,val[v]); } void sol(int x) { int y,z; for(int i=hd[x];i;i=nex[i]) { y=to[i],sol(y); if(se[id[x]].size()<se[id[y]].size()) { swap(id[x],id[y]); } for(int j=0;j<se[id[y]].size();++j) { int p=se[id[y]][j]; int tmp=mx[x]+query(rt[id[x]],22,val[p]); ins(rt[id[x]],22,val[p]); se[id[x]].pb(p); if(tmp>ans) { ans=tmp; } } } } int main() { // setIO("input"); init(); scanf("%d%s",&n,str+1); for(int i=1;i<=n;++i) { scanf("%d",&val[i]); } for(int i=1;i<=n;++i) { extend(str[n-i+1]-'a',n-i+1); } for(int i=2;i<=tot;++i) { add(pre[i],i); } sol(1); printf("%d\n",ans); return 0; }