bzoj 3145 [Feyat cup 1.5]Str

权限题

这题暴力就是两个串分别枚举点\(i,j\),假设这两个点不同,那么剩下的部分就是尽量越长越好,所以这部分答案就是第一个串\(i-1\)前缀和第二个串\(j-1\)前缀的\(lcs\)+第一个串\(i+1\)后缀和第二个串\(j+1\)后缀的\(lcp\)+1

考虑把两个串接起来,中间用个没用过的字符隔开,那么两个串前缀的\(lcs\)就可以先在前缀树上找到对应的两个点,然后就是两点\(lca\)\(length\).后缀的\(lcp\)也类似.那么答案就是\(\max (lcs(a_{i-1},a_{j-1})+lcp(b_{i+1},b_{j+1}))\),其中\(a_i\)为某前缀在前缀树上对应点,\(b_i\)为后缀在后缀树上对应点

所以这个问题变成了询问所有点对在两棵树上lca深度和的最大值.可以dfs第一棵树,然后处理第一棵树上lca为\(x\)的点对,每次把儿子内点在第二棵树的信息合并过来,就能求解.首先,两个子树合并,为了保证复杂度,需要启发式合并,然后在合并时算贡献.现在就是要知道某个点和一个点集内点在第二棵树上的lca最大深度,容易发现一定和dfs序相邻的点lca最深.所以一个点维护的是子树内点在第二棵树上以dfs序为关键字的set,每次二分查找前驱后继即可

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=2e5+10;
LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
struct SAM
{
    int fa[N<<1],ch[N<<1][28],len[N<<1],la,tt;
    int ps[N],px[N<<1],sz[N<<1],de[N<<1],hs[N<<1],top[N<<1],dfn[N<<1],ti;
    vector<int> e[N<<1];
    SAM(){la=tt=1;}
    void extd(int cx,int ii)
    {
        int np=++tt,p=la;
        len[np]=len[p]+1,ps[ii]=np,px[np]=ii,la=np;
        while(!ch[p][cx]) ch[p][cx]=np,p=fa[p];
        if(!p) fa[np]=1;
        else
        {
            int q=ch[p][cx];
            if(len[q]==len[p]+1) fa[np]=q;
            else
            {
                int nq=++tt;
                fa[nq]=fa[q],len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[np]=fa[q]=nq;
                while(ch[p][cx]==q) ch[p][cx]=nq,p=fa[p];
            }
        }
    }
    void dfs1(int x)
    {
        sz[x]=1;
        vector<int>::iterator it;
        for(it=e[x].begin();it!=e[x].end();++it)
        {
            int y=*it;
            de[y]=de[x]+1,dfs1(y);
            sz[x]+=sz[y],hs[x]=sz[hs[x]]>sz[y]?hs[x]:y;
        }
    }
    void dfs2(int x,int ntp)
    {
        dfn[x]=++ti,top[x]=ntp;
        if(hs[x]) dfs2(hs[x],ntp);
        vector<int>::iterator it;
        for(it=e[x].begin();it!=e[x].end();++it)
        {
            int y=*it;
            if(y==hs[x]) continue;
            dfs2(y,y);
        }
    }
    void inii()
    {
        for(int i=2;i<=tt;++i) e[fa[i]].push_back(i);
        dfs1(1),dfs2(1,1);
    }
    int glca(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(de[top[x]]<de[top[y]]) swap(x,y);
            x=fa[top[x]];
        }
        return de[x]<de[y]?x:y;
    }
}t1,t2;
int n,m,ans;
char cc[N];
struct node
{
    int x;
    bool operator < (const node &bb) const {return t2.dfn[x]<t2.dfn[bb.x];}
};
multiset<node> s1[N<<1],s2[N<<1];
multiset<node>::iterator i1,i2,ft,nt,zr;
int id[N<<1],tot;
void dfs(int x)
{
    vector<int>::iterator it;
    id[x]=x;
    if(t1.px[x])
    {
        if(t1.px[x]+1<=n+1) s1[id[x]].insert((node){t2.ps[t1.px[x]+2]});
        else if(t1.px[x]+1>n+2&&t1.px[x]+2<=n+m+2) s2[id[x]].insert((node){t2.ps[t1.px[x]+2]});
    }
    for(it=t1.e[x].begin();it!=t1.e[x].end();++it)
    {
        int y=*it;
        dfs(y);
        if(s1[id[x]].size()+s2[id[x]].size()<s1[id[y]].size()+s2[id[y]].size()) swap(id[x],id[y]);
        bool fg1=!s1[id[x]].empty(),fg2=!s2[id[x]].empty();
        if(fg2)
        {
            for(i1=s1[id[y]].begin();i1!=s1[id[y]].end();++i1)
            {
                int xx=(*i1).x;
                nt=s2[id[x]].upper_bound(*i1);
                if(nt!=s2[id[x]].begin()) ft=--nt,++nt;
                else ft=zr;
                if((*ft).x) ans=max(ans,t1.len[x]+t2.len[t2.glca((*ft).x,xx)]+1);
                if(nt!=s2[id[x]].end()) ans=max(ans,t1.len[x]+t2.len[t2.glca((*nt).x,xx)]+1);
            }
        }
        if(fg1)
        {
            for(i2=s2[id[y]].begin();i2!=s2[id[y]].end();++i2)
            {
                int xx=(*i2).x;
                nt=s1[id[x]].upper_bound(*i2);
                if(nt!=s1[id[x]].begin()) ft=--nt,++nt;
                else ft=zr;
                if((*ft).x) ans=max(ans,t1.len[x]+t2.len[t2.glca((*ft).x,xx)]+1);
                if(nt!=s1[id[x]].end()) ans=max(ans,t1.len[x]+t2.len[t2.glca((*nt).x,xx)]+1);
            }
        }
        for(i1=s1[id[y]].begin();i1!=s1[id[y]].end();++i1) s1[id[x]].insert(*i1);
        for(i2=s2[id[y]].begin();i2!=s2[id[y]].end();++i2) s2[id[x]].insert(*i2);
    }
}

int main()
{
    cc[1]='|';
    scanf("%s",cc+2);
    n=strlen(cc+2);
    cc[n+2]='{';
    scanf("%s",cc+n+3);
    m=strlen(cc+n+3);
    for(int i=1;i<=n+m+2;++i) t1.extd(cc[i]-'a',i);
    t1.inii();
    for(int i=n+m+2;i;--i) t2.extd(cc[i]-'a',i);
    t2.inii();
    s1[0].insert((node){0});
    zr=s1[0].begin();
    dfs(1);
    printf("%d\n",ans);
    return 0; 
}

转载于:https://www.cnblogs.com/smyjr/p/11524303.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值