[十二省联考2019]字符串问题 后缀自动机 + 拓扑排序 + 最长路 + 倍增

题目描述:
给定一个长串 $S$,给定若干 $S$ 的子串 $a_{i}$, $b_{i}$,再给出一些 $a$ 串和 $b$ 串的支配关系.
构造一个长度最长的字符串,使得:
字符串只由 $a_{i}$ 组成.
当且仅当 $a_{i}$ 所支配的一个串 $b_{i}$ 为 $a_{j}$ 的前缀,才可将 $a_{j}$ 连到 $a_{i}$ 后面.


首先,对于求前缀,我们可以对 $S$ 的反串建立后缀自动机,这样每个 $f_{i}$ 将会转移到当前串的一个前缀,而不是后缀.

第一步,先将所有的 $a$串和$b$串都定位到后缀自动机上.
对于这一步,我们使用倍增算法.

第二部,我们将支配边进行连边(即再后缀树中对应的点和点).

顺着后缀树中的边和支配边进行转移即可.
细节部分看一下代码.

 

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue> 
#include <vector> 
#define setIO(s) freopen(s".in","r",stdin)// ,freopen(s".out","w",stdout) 
#define maxn 1500001 
using namespace std;
char str[maxn]; 
int debug; 
int length,na,nb,m;  
namespace SAM{
    #define N 29 
    #define ll long long 
    #define L l=length-l+1
    #define R r=length-r+1 
    int last,tot;     
    vector<int>ed[maxn];        
    int la[maxn],ra[maxn],h[maxn];     
    int ch[maxn][N],dis[maxn]; 
    int f[maxn],trace[maxn],mk[maxn],pos[maxn];  
    int F[24][maxn];     
    int head[maxn],to[maxn],nex[maxn],edges; 
    int ge[maxn]; 
    int vis[maxn]; 
    int trc; 
    queue<int>Q; 
    int du[maxn]; 
    long long DP[maxn]; 
    int idx[maxn]; 
    int cmp(int i,int j){   if(h[i]==h[j]) return i>j; return h[i]<h[j];         }
    void add(int u,int v){
        nex[++edges] = head[u],head[u]=edges,to[edges]=v;    
        mk[edges]=0;   
        //if(!debug) printf("%d %d\n",u,v); 
    }
    //SAM of S 
    void ins(int c,int i){          
        int np = ++tot, p = last; last = np; dis[np] = i; 
   	    while(p && !ch[p][c]) ch[p][c] = np, p = f[p];     
	    if(!p) f[np] = 1;
	    else {      
	        int q = ch[p][c];	
	        if(dis[q] == dis[p] + 1) f[np] = q;
	        else {
		        int nq = ++tot; 
		        dis[nq] = dis[p] + 1; 
                memcpy(ch[nq],ch[q],sizeof(ch[q])); 
		        f[nq] = f[q], f[np] = f[q] = nq;   
		        while(p && ch[p][c] == q) ch[p][c] = nq,p = f[p]; 
	        }
	    }
        trace[i] = np; 
    } 
    void DFS(int u){           
        F[0][u]=f[u];
        for(int i=1;i<24;++i) F[i][u]=F[i-1][F[i-1][u]];  
        int sz=ed[u].size(); 
        for(int i=0;i<sz;++i) DFS(ed[u][i]); 
        ed[u].clear(); 
    }    
    void build(){
        int l,r;scanf("%d",&na);
        for(int i=1;i<=na;++i){
            scanf("%d%d",&l,&r); L;R; swap(l,r); 
            la[i]=l,ra[i]=r,h[i]=r-l+1;   
        } scanf("%d",&nb); 
        for(int i=1;i<=nb;++i)  {
            scanf("%d%d",&l,&r); L;R; swap(l,r); 
            la[i+na]=l,ra[i+na]=r,h[i+na]=r-l+1;  
        }for(int i=2;i<=tot;++i) ed[f[i]].push_back(i); 
        trc=tot; 
        DFS(1);      
        for(int i=1;i<=na+nb;++i) {
            int p=trace[ra[i]];  
            for(int j=23;j>=0;--j) 
                if(dis[F[j][p]] >= h[i]) 
                    p=F[j][p];    
            if(dis[p]==h[i]) 
                pos[i]=p;             
            else 
                ed[p].push_back(i); 
        }
    }
    void sol(){
        int nn=tot; 
        for(int i=2;i<=nn;++i){   
            if(ed[i].size()==0){       
                add(f[i],i);           
                continue; 
            }
            int p=0;                
            int sz=ed[i].size();        
            for(int j=0;j<sz;++j) ge[++p]=ed[i][j];           //  
            sort(ge+1,ge+1+p,cmp);                           //将该点对应字符串按长度排序       
            int lst=f[i]; 
            for(int j=1;j<=p;++j) {            
                add(lst,++tot); 
                dis[tot] = h[ge[j]];   
                pos[ge[j]]=tot;  
                lst=tot; 
            }
            add(pos[ge[p]],i); 
            ed[i].clear(); 
        }
    }
    void get(){
        int a,b; 
        scanf("%d%d",&a,&b); 
        add(pos[a],pos[b+na]);   
        mk[edges]=1;  
    }     
    void solve(){
        Q.push(1); 
        du[1]=0; 
        long long  ans=0; 
        for(int i=1;i<=edges;++i) ++du[to[i]]; 
        while(!Q.empty()){
            int u=Q.front(); Q.pop(); 
            for(int i=head[u];i;i=nex[i]){
                --du[to[i]];                       
                long long ed=DP[u]+dis[to[i]]-(mk[i]?0:dis[u]); 
                DP[to[i]]=max(DP[to[i]],ed);                           
                if(du[to[i]]==0) Q.push(to[i]); 
            }
        }    
        for(int i=1;i<=tot;++i) if(du[i]!=0) {
            printf("-1\n"); 
            return; 
        }
        for(int i=1;i<=na;++i)  ans=max(ans,DP[pos[i]]);      
        printf("%lld\n",ans);     
    }
    void init(){
        for(int i=1;i<=tot;++i) DP[i]=0; 
        for(int i=1;i<=tot;++i) dis[i]=0;    
        for(int i=1;i<=edges;++i) mk[i]=0;        
        for(int i=1;i<=tot;++i) head[i]=0;  
        for(int i=0;i<=tot;++i) du[i]=0;      
        for(int i=1;i<=trc;++i) for(int j=0;j<=22;++j) F[j][i]=0; 
        for(int i=1;i<=trc;++i) memset(ch[i],0,sizeof(ch[i]));     
        last=tot=1,edges=0;     
    }
};
int main(){
    //setIO("string9");   
    int T; 
    scanf("%d",&T);   
    while(T--){    
        debug=T; 
        SAM::last=SAM::tot=1;            
        scanf("%s",str+1),length=strlen(str+1);             
        for(int i=length;i;--i) SAM::ins(str[i]-'a',length-i+1);          
        SAM::trc=SAM::tot; 
        SAM::build();   
        SAM::sol(); 
        scanf("%d",&m); 
        for(int i=1;i<=m;++i) SAM::get();     
        SAM::solve(); 
        SAM::init(); 
    }
    return 0; 
}

  

posted @ 2019-04-09 20:34  EM-LGH  阅读(235)  评论(2编辑  收藏  举报