zoukankan      html  css  js  c++  java
  • 【HDU4117】GRE Words-AC自动机+线段树优化DP

    测试地址:GRE Words
    题目大意:给定一个字符串序列,每个字符串有一个权值。求一个子序列(可以为空),使得序列中前一项总是后一项的子串,并且字符串的权值和最大。
    做法:本题需要用到AC自动机+线段树优化DP。
    很容易看出一个DP,但我们不能暴力找到可以转移的点,因此我们需要观察能转移到字符串si的点的性质。
    首先对这些串建出AC自动机,我们发现,可以转移到字符串si的点在fail树上是该字符串的所有前缀点到根的链。这样考虑转移十分麻烦,但是如果我们把转移看成,每处理一个字符串,它就会对它在fail树中的子树上所有的点作出贡献,于是把fail树的DFS序求出来后,这就是一个区间chkmax(即进行ai=max(ai,b)这样的操作)的问题。而对于每个字符串转移时,对它的所有前缀节点进行单点询问即可。显然我们可以用线段树维护这样的东西。
    于是我们就以O(totlogtot)的时间复杂度解决了这个问题,其中tot表示字符串总长。
    值得注意的是本题的空间限制很紧,只有32MB,因此trie不能直接开数组存,而是应该用链表一样的形式来用时间换空间。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int T,n,w[20010];
    int rt,totp,first[300010],triefirst[300010],tot;
    int pt[400010],L[20010],R[20010];
    int in[300010],out[300010],tim;
    int q[300010],h,t,fail[300010];
    int seg[1200010],tag[1200010];
    struct edge
    {
        int v,next;
    }e[300010];
    struct trieedge
    {
        int v,next,type;
    }ed[300010];
    char s[300010];
    
    void trieinsert(int a,int b,int type)
    {
        ed[++tot].v=b;
        ed[tot].next=triefirst[a];
        ed[tot].type=type;
        triefirst[a]=tot;
    }
    
    int ch(int v,int type)
    {
        for(int i=triefirst[v];i;i=ed[i].next)
            if (ed[i].type==type) return ed[i].v;
        return -1;
    }
    
    void insert(int v,int x,int step,int len)
    {
        pt[++R[x]]=v;
        if (step==len) return;
        int nxt=ch(v,s[step]-'a');
        if (nxt==-1)
        {
            nxt=++totp;
            trieinsert(v,nxt,s[step]-'a');
        }
        insert(nxt,x,step+1,len);
    }
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    void buildAC()
    {
        q[1]=rt,h=t=1;
        fail[rt]=0;
        while(h<=t)
        {
            int v=q[h++];
            for(int x=triefirst[v];x;x=ed[x].next)
            {
                int i=ed[x].type,p=fail[v];
                int y=ed[x].v;
                while(p&&ch(p,i)==-1) p=fail[p];
                if (p) fail[y]=ch(p,i);
                else fail[y]=rt;
                insert(fail[y],y);
                q[++t]=y;
            }
        }
    }
    
    void dfs(int v)
    {
        in[v]=++tim;
        for(int i=first[v];i;i=e[i].next)
            dfs(e[i].v);
        out[v]=tim;
    }
    
    void pushdown(int no)
    {
        if (tag[no]>0)
        {
            seg[no<<1]=max(seg[no<<1],tag[no]);
            tag[no<<1]=max(tag[no<<1],tag[no]);
            seg[no<<1|1]=max(seg[no<<1|1],tag[no]);
            tag[no<<1|1]=max(tag[no<<1|1],tag[no]);
            tag[no]=0;
        }
    }
    
    void pushup(int no)
    {
        seg[no]=max(seg[no<<1],seg[no<<1|1]);
    }
    
    void buildtree(int no,int l,int r)
    {
        seg[no]=tag[no]=0;
        if (l==r) return;
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
    }
    
    void modify(int no,int l,int r,int s,int t,int x)
    {
        if (l>=s&&r<=t)
        {
            seg[no]=max(seg[no],x);
            tag[no]=max(tag[no],x);
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) modify(no<<1,l,mid,s,t,x);
        if (t>mid) modify(no<<1|1,mid+1,r,s,t,x);
        pushup(no);
    }
    
    int query(int no,int l,int r,int x)
    {
        if (l==r) return seg[no];
        int mid=(l+r)>>1;
        pushdown(no);
        if (x<=mid) return query(no<<1,l,mid,x);
        else return query(no<<1|1,mid+1,r,x);
    }
    
    int main()
    {
        scanf("%d",&T);
        int Case=0;
        while(T--)
        {
            rt=totp=1;
            memset(first,0,sizeof(first));
            memset(triefirst,0,sizeof(triefirst));
            tot=0;
    
            scanf("%d",&n);
            R[0]=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%s%d",s,&w[i]);
                L[i]=R[i-1]+1;
                R[i]=R[i-1];
                insert(rt,i,0,strlen(s));
            }
    
            tim=0;
            tot=0;
            buildAC();
            dfs(rt);
            buildtree(1,1,totp);
            int ans=0;
            for(int i=1;i<=n;i++)
            {
                int mx=0;
                for(int j=L[i];j<=R[i];j++)
                    mx=max(mx,query(1,1,totp,in[pt[j]]));
                ans=max(ans,mx+w[i]);
                modify(1,1,totp,in[pt[R[i]]],out[pt[R[i]]],mx+w[i]);
            }
    
            Case++;
            printf("Case #%d: %d
    ",Case,ans);
        }
    
        return 0; 
    }
  • 相关阅读:
    iOS BUG整理-记录我近期视频开发遇到的问题
    iOS 翻译-UIWebView的基本简介-官方文档翻译
    iOS 翻译-Xcode使用-文档翻译
    4.redis设计与实现--跳跃表
    3.redis设计与实现--字典
    2.redis设计与实现--链表
    1.redis设计与实现--简单动态字符串
    12.编码问题讨论
    11.nginx upload module + python django 后台 实现视频上传与切片
    10.nginx+ffmpeg上搭建HLS切片
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793265.html
Copyright © 2011-2022 走看看