【SAM/SA+主席树+拓扑DP】LOJ3049 [十二省联考 2019] 字符串问题

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/89260696

【题目】
LOJ
给定一个长度为 n n 的字符串,在其中选择 n a na 段区间作为 A A 字符串集,选出 n b nb 段区间作为 B B 字符串集,区间之间互不影响。再给定 m m 组支配关系 ( x , y ) (x,y) ,表示 A A 集中第 x x 个字符串能支配 B B 集中第 y y 个字符串。求一个长度最大的字符串 T T ,存在一个分割 T = t 1 + t 2 + + t k T=t_1+t_2+\cdots +t_k ,满足:

  • 所有 t i t_i 均为 A A 中的一个字符串,不妨设 t i = A i d i t_i=A_{id_i}
  • 对于 t i , t i + 1 t_i,t_{i+1} ,存在一个 A i d i A_{id_i} 支配的 B B 集中的串,使得后者是 t i + 1 t_{i+1} 的前缀
    T |T| ,或无限长输出 1 -1
    n , n a , n b 2 × 1 0 5 n,na,nb\leq 2\times 10^5

【解题思路】
无限长的题目,又是要我们在 DAG \text{DAG} 上求最长路了,有环就是 1 -1
暴力的建图就是对于每个支配关系连一条有向边,每个 B B 连向它是前缀的 A A
这样图的规模就是 O ( n 2 ) O(n^2) 的了,考虑优化建图。

首先前缀可以反转后转为后缀,那么上后缀自动机。
后缀自动机的 parent \text{parent} 树中,若一个点 x x 是另一个节点 y y 的祖先,则 x x y y 的后缀。
那么一个显然的想法是将后缀自动机上的每个点连向它的儿子,这样就可以满足后缀关系了。实际上是对于从父亲长度最长的 B B
一个问题是一个节点可能代表多个子串,不过这个也不难想到,我们将所有 A A 拆成入度和出度,不妨设为 A 0 , A 1 A_0,A_1 。我们给每个 A 1 A_1 带上点权为长度。对于每个 SAM \text{SAM} 的节点,我们将其代表的所有子串 B , A 0 B,A_0 按长度从小到大排序,长度相同 B B 优先(可以贡献),它们的点权都为 0 0 。然后依次连接。每个 A 0 A_0 A 1 A_1 连边。一点的开头节点向其父亲的结尾节点连边即可。
这样相当于选择一个位置匹配走下去,十分简洁且高妙。
然后拓扑排序最长路即可。

复杂度 O ( n log n ) O(n\log n)

还有后缀数组的做法,可以观察到每个 B B 串对应的所有 A A 串在后缀数组上都是连续的一段区间,那么线段树优化连边可以得到 B < A |B|<|A| 的分数。现在没有这个性质的话,区间连边变成了矩形连边,那这一维限制就用主席树来处理掉。
复杂度是一样的(吧)。

【参考代码】
SAM

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

typedef long long ll;
const int N=4e5+10,M=2e6+10;

namespace IO
{
    int read()
    {
        int ret=0;char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
        return ret;
    }
    void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
    void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace SAM
{
    struct SAM
    {
        int sz,las,ch[N][26],fa[N],siz[N],mx[N];
        void init(){las=sz=1;}
        void extend(int x)
        {
            int p,q,np,nq;
            p=las;las=np=++sz;mx[np]=mx[p]+1;++siz[las];
            for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;
            if(!p) fa[np]=1;
            else 
            {
                q=ch[p][x];
                if(mx[q]==mx[p]+1) fa[np]=q;
                else
                {
                    nq=++sz;mx[nq]=mx[p]+1;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    fa[nq]=fa[q];fa[q]=fa[np]=nq;
                    for(;p && ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
                }
            }
        }
        void clear()
        {
            for(int i=1;i<=sz;++i) 
            {
                mx[i]=siz[i]=fa[i]=0;
                memset(ch[i],0,sizeof(ch[i]));
            }
            sz=las=1;
        }
    }T;
}
using namespace SAM;

namespace DreamLolita
{
    int n,m,na,nb,cnt,tot;
    int val[M],du[M],head[M],pos[M];
    int fa[N][20];
    ll f[M];
    char s[N];
    vector<int>E[N];
    queue<int>q;
    struct data
    {
        int len,id;
        data(int _l=0,int _i=0):len(_l),id(_i){}
        bool operator <(const data&rhs)const{return len==rhs.len?id>rhs.id:len<rhs.len;}
    };
    vector<data>vec[N];
    struct Tway{int v,nex;}e[M];
    void add(int u,int v){e[++tot]=Tway{v,head[u]};head[u]=tot;++du[v];}
    void AllClear()
    {
        for(int i=1;i<=T.sz;++i) vec[i].clear(),E[i].clear();
        for(int i=1;i<=cnt;++i) head[i]=val[i]=du[i]=f[i]=pos[i]=0,memset(fa[i],0,sizeof(fa[i]));
        cnt=tot=0;T.clear();
    }
    void dfs1(int x)
    {
        for(int i=1;i<19;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
        for(auto v:E[x]) dfs1(v);
    }
    void dfs2(int x)
    {
        if(vec[x].empty()) vec[x].pb(data(0,++cnt));
        sort(vec[x].begin(),vec[x].end());

        for(int i=0;i<(int)vec[x].size()-1;++i) add(vec[x][i].id,vec[x][i+1].id);
        if(x>1) add(vec[fa[x][0]].back().id,vec[x].front().id);
        for(auto v:E[x]) dfs2(v);
    }
    int getpos(int l,int r)
    {
        int x=pos[r],len=r-l+1;//printf("%d\n",x);
        for(int i=18;~i;--i) if(T.mx[fa[x][i]]>=len) x=fa[x][i];
        return x;
    }
    void buildmap()
    {
        na=read();
        for(int i=1;i<=na;++i) 
        {
            int r=n-read()+1,l=n-read()+1,x=getpos(l,r);val[i]=r-l+1;
            add(i+na,i);vec[x].pb(data(r-l+1,i+na));
        }
        nb=read();
        for(int i=1;i<=nb;++i)
        {
            int r=n-read()+1,l=n-read()+1,x=getpos(l,r);
            vec[x].pb(data(r-l+1,i+na*2));
        }
        cnt=na*2+nb;dfs2(1);m=read();
        for(int i=1,x,y;i<=m;++i) x=read(),y=read(),add(x,y+na*2); 
    }
    void topsort()
    {
        int IN=0;ll ans=0;
        for(int i=1;i<=cnt;++i) if(!du[i]) q.push(i),f[i]=val[i];
        while(!q.empty())
        {
            int x=q.front();q.pop();++IN;ans=max(ans,f[x]);
            for(int i=head[x];i;i=e[i].nex)
            {
                int v=e[i].v;f[v]=max(f[v],f[x]+val[v]);
                if(!--du[v]) q.push(v);
            }
        }
        if(IN^cnt) puts("-1"); else writeln(ans);
    }
    void solve()
    {
        scanf("%s",s+1);n=strlen(s+1);reverse(s+1,s+n+1);
        for(int i=1;i<=n;++i) T.extend(s[i]-'a'),pos[i]=T.las;
        for(int i=2;i<=T.sz;++i) fa[i][0]=T.fa[i],E[T.fa[i]].pb(i);
        dfs1(1);buildmap();topsort();
    }
    void solution()
    {
        AllClear();solve();
    }
}

int main()
{
#ifdef Durant_Lee
    freopen("LOJ3049.in","r",stdin);
    freopen("LOJ3049.out","w",stdout);
#endif
    int T=read();
    while(T--) DreamLolita::solution(); 
    return 0;
}

别人的SA

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
#include <set>
#include <map>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;

using std::cerr;
using std::endl;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int MAXN=200005;

int n,m,na,nb,b[MAXN],l[MAXN],pos[MAXN],pos2[MAXN],odr[MAXN];
int siz,sa[MAXN],rk[MAXN<<1],sc[MAXN<<1],bk[MAXN],ht[MAXN],st[20][MAXN];
int ecnt,tot,root[MAXN],lc[MAXN*20],rc[MAXN*20],head[MAXN*20],deg[MAXN*20],w[MAXN*20],loc,ql,qr;
char s[MAXN];
LL f[MAXN*20];
std::queue<int> q;

struct node{
    int x,id,len;
}a[MAXN];

struct Edge{
    int to,nxt;
}e[MAXN*50];

inline void add_edge(int bg,int ed){
    ++ecnt;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    head[bg]=ecnt;
    ++deg[ed];
}

void radix_sort(){
    rin(i,1,siz)bk[i]=0;
    rin(i,1,n)++bk[rk[i]];
    rin(i,1,siz)bk[i]+=bk[i-1];
    irin(i,n,1)sa[bk[rk[sc[i]]]--]=sc[i];
}

void suffix_sort(){
    siz=26;
    rin(i,1,n)rk[i]=s[i],sc[i]=i;
    radix_sort();
    for(int wd=1;;wd<<=1){
        int cnt=0;
        rin(i,1,wd)sc[++cnt]=n-wd+i;
        rin(i,1,n)if(sa[i]-wd>0)sc[++cnt]=sa[i]-wd;
        radix_sort();
        std::swap(rk,sc);
        rk[sa[1]]=cnt=1;
        rin(i,2,n)rk[sa[i]]=(sc[sa[i-1]]==sc[sa[i]]&&sc[sa[i-1]+wd]==sc[sa[i]+wd]?cnt:++cnt);
        siz=cnt;
        if(cnt==n)return;
    }
}

inline void calc_height(){
    int preh=0;
    rin(i,1,n){
        if(rk[i]==1){
            preh=ht[rk[i]]=0;
            continue;
        }
        int now=std::max(preh-1,0);
        while(s[sa[rk[i]-1]+now]==s[i+now])++now;
        preh=ht[rk[i]]=now;
    }
}

void build_st(){
    rin(i,1,n)st[0][i]=ht[i];
    int lim=log2(n);
    rin(i,1,lim)rin(j,1,n-(1<<i)+1)
        st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}

inline int lcp(int x,int y){
    if(x==y)return n-x+1;
    x=rk[x],y=rk[y];
    if(x>y)std::swap(x,y);
    ++x;int lim=log2(y-x+1);
    return std::min(st[lim][x],st[lim][y-(1<<lim)+1]);
}

inline bool cmp(node x,node y){
    return rk[x.x]<rk[y.x];
}

inline bool cmp2(int x,int y){
    return a[x].len>a[y].len;
}

#define mid ((l+r)>>1)

int ins(int pre,int l,int r){
    int o=++tot;
    lc[o]=lc[pre],rc[o]=rc[pre];
    if(l==r){
        pos[l]=o;
        w[o]=a[l].len;
        return o;
    }
    if(loc<=mid)lc[o]=ins(lc[pre],l,mid);
    else rc[o]=ins(rc[pre],mid+1,r);
    add_edge(o,lc[o]);
    add_edge(o,rc[o]);
    return o;
}

void conn(int o,int l,int r,int frm){
    if(ql>qr)return;
    if(!o)return;
    if(ql<=l&&r<=qr){
        add_edge(frm,o);
        return;
    }
    if(mid>=ql)conn(lc[o],l,mid,frm);
    if(mid<qr)conn(rc[o],mid+1,r,frm);
}

#undef mid

LL topo(){
    int cnt=0;LL ret=0;
    while(!q.empty())q.pop();
    rin(i,1,tot)if(!deg[i])q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();++cnt;
        f[x]+=w[x];
        ret=std::max(ret,f[x]);
        trav(i,x){
            int ver=e[i].to;
            f[ver]=std::max(f[ver],f[x]);
            --deg[ver];
            if(!deg[ver])q.push(ver);
        }
    }
    if(cnt<tot)return -1;
    else return ret;
}

void clear(){
    ecnt=tot=0;
    memset(head,0,sizeof head);
    memset(deg,0,sizeof deg);
    memset(w,0,sizeof w);
    memset(f,0,sizeof f);
}

int main(){
    int T=read();
    while(T--){
        clear();
        scanf("%s",s+1);
        n=strlen(s+1);
        rin(i,1,n)s[i]-='a'-1;
        suffix_sort();
        calc_height();
        build_st();
        na=read();
        rin(i,1,na){
            int la=read(),ra=read();
            a[i]=(node){la,i,ra-la+1};
        }
        std::sort(a+1,a+na+1,cmp);
        rin(i,1,na)pos2[a[i].id]=i,odr[i]=i;
        std::sort(odr+1,odr+na+1,cmp2);
        root[n+1]=0;int ptr=0;
        irin(i,n,1){
            root[i]=root[i+1];
            while(ptr<na&&a[odr[ptr+1]].len==i){
                loc=odr[++ptr];
                root[i]=ins(root[i],1,na);
            }
        }
        nb=read();
        rin(i,1,nb){
            int lb=read(),rb=read();
            b[i]=lb,l[i]=rb-lb+1;
            w[tot+i]=0;
        }
        rin(i,1,nb){
            int ll=1,rr=std::upper_bound(a+1,a+na+1,(node){b[i],0,0},cmp)-a-1,ret=rr+1;
            while(ll<=rr){
                int midd=((ll+rr)>>1);
                if(lcp(a[midd].x,b[i])>=l[i])ret=midd,rr=midd-1;
                else ll=midd+1;
            }
            ql=ret;
            ll=std::lower_bound(a+1,a+na+1,(node){b[i],0,0},cmp)-a,rr=na,ret=ll-1;
            while(ll<=rr){
                int midd=((ll+rr)>>1);
                if(lcp(a[midd].x,b[i])>=l[i])ret=midd,ll=midd+1;
                else rr=midd-1;
            }
            qr=ret;
            conn(root[l[i]],1,na,tot+i);
        }
        m=read();
        rin(i,1,m){
            int u=read(),v=read();
            add_edge(pos[pos2[u]],tot+v);
        }
        tot+=nb;
        printf("%lld\n",topo());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/89260696
今日推荐