往事 - SAM - 启发式合并

题目大意:给你个Trie,求两个从根出发的字符串(不一定要到达叶子),使得LCP+LCS(这个是最长公共后缀)最大。
题解:建出SAM后,考虑parent树中每次合并两颗子树,启发式的把小的插到大的里面,显然每次插入的时候只会取dfs序的前驱和后继做LCA来更新答案。两个log。


#include
#include
#include
#include
#include
#include
#include
#define LOG 20
#define N 200010
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) for(int i=0;i<(int)v.size();i++)
using namespace std;
typedef pair<int,int> pii;
typedef unordered_map<int,int> umap;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct edges{
    int to,pre;
}e[N<<1];int h[N<<1],etop;vector g[N];queue<int> q;
int in[N],tms[N],ans=0,Log[N],pos[N],d[N],up[N][LOG],dfc;
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
inline void build_TRIE(int n,int x=0) { rep(i,2,n) x=inn(),g[x].push_back(mp(i,inn())),Log[i]=Log[i>>1]+1; }
inline int get_up(int x,int y=0)
{
    tms[in[x]=++dfc]=x;rep(i,1,Log[d[x]]) up[x][i]=up[up[x][i-1]][i-1];
    Rep(i,g[x]) d[y=g[x][i].fir]=d[x]+1,up[y][0]=x,get_up(y);return 0;
}
inline int getLCA(int x,int y)
{
    if(d[x]for(int i=Log[d[x]];i>=0;i--)
        if(up[x][i]&&d[up[x][i]]>=d[y]) x=up[x][i];
    if(x==y) return x;
    for(int i=Log[d[x]];i>=0;i--)
        if(up[x][i]^up[y][i]) x=up[x][i],y=up[y][i];
    return up[x][0];
}
struct SAM{
    int val[N<<1],fa[N<<1],rt,node_cnt;umap *ch[N<<1];set<int> *s[N<<1];
    SAM(){ rt=node_cnt=1,val[1]=0,ch[1]=new umap,s[1]=new set<int>; }
    inline int new_node(int v,int x=0) { return x=++node_cnt,val[x]=v,ch[x]=new umap,s[x]=new set<int>,x; }
    inline int extend(int p,int w,int x)
    {
        int np=new_node(val[p]+1);
        while(p&&!ch[p]->count(w)) (*ch[p])[w]=np,p=fa[p];
        if(!p) fa[np]=rt;
        else{
            int q=(*ch[p])[w],v=val[p]+1;
            if(val[q]==v) fa[np]=q;
            else{
                int nq=new_node(v);(*ch[nq])=(*ch[q]);fa[nq]=fa[q],fa[q]=fa[np]=nq;
                while(p&&ch[p]->count(w)&&(*ch[p])[w]==q) (*ch[p])[w]=nq,p=fa[p];
            }
        }
        return s[np]->insert(in[x]),np;
    }
    int getans(int x=0)
    {
        if(!x) { rep(i,1,node_cnt) if(fa[i]) add_edge(fa[i],i);return getans(rt); }
        for(int i=h[x],y;i;i=e[i].pre)
        {
            getans(y=e[i].to);if(s[x]->size()size()) s[x]->swap(*s[y]);
            for(sit it=s[y]->begin();it!=s[y]->end();it++)
            {
                 sit nxt=s[x]->upper_bound(*it);
                 if(nxt!=s[x]->end()) ans=max(ans,d[getLCA(tms[*nxt],tms[*it])]+val[x]);
                 s[x]->insert(*it);sit pre=s[x]->lower_bound(*it);
                 if(pre!=s[x]->begin()) --pre,ans=max(ans,d[getLCA(tms[*pre],tms[*it])]+val[x]);
            }
        }
        return 0;
    }
}s;
inline int build_SAM()
{
    while(!q.empty()) q.pop();q.push(1),pos[1]=1;
    while(!q.empty())
    {
        int x=q.front(),y;q.pop();
        Rep(i,g[x]) y=g[x][i].fir,q.push(y),
            pos[y]=s.extend(pos[x],g[x][i].sec,y);
    }
    return 0;
}
int main() { return build_TRIE(inn()),get_up(1),build_SAM(),s.getans(),!printf("%d\n",ans); }

你可能感兴趣的:(广义后缀自动机,启发式合并)