LOJ#6198. 謝特 SAM+啟發式合併+01trie
阿新 • • 發佈:2020-07-20
並不難的一道字串題.
顯然後綴自動機上進行字典樹的啟發式合併.
但是一定注意,題中要求的是兩個字尾的 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; }