• [十二省联考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
    ",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
    "); 
                return; 
            }
            for(int i=1;i<=na;++i)  ans=max(ans,DP[pos[i]]);      
            printf("%lld
    ",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; 
    }
    

      

  • 相关阅读:
    1509 加长棒
    51Nod 1158 全是1的最大子矩阵
    P2953 [USACO09OPEN]牛的数字游戏Cow Digit Game
    P3384 【模板】树链剖分
    北京集训DAY3
    北京集训DAY2
    北京集训DAY1
    51Nod 1422 沙拉酱前缀 二分查找
    51Nod 1109 01组成的N的倍数
    51Nod 1043 幸运号码 数位DP
  • 原文地址:https://www.cnblogs.com/guangheli/p/10679544.html
Copyright © 2020-2023  润新知