hdu 6583 后缀自动机

hdu 6583 后缀自动机
题意:
构造字符串凭空加一个花费q,复制之前的一段花费p。
思路:
这是个假题解,因为并没有卡进1500ms,差个100~200ms,这是真的气。
dp方程转移就是贪心的找最长的之前出现过的后缀。根据parent树的性质,每个父亲都是儿子的后缀,只需要知道父亲第一次出现的right就好了。
当个后缀自动机的模板,但是代码写搓了TLE,对拍了一下思路没什么问题。
(这题还有个加强版,\(复制的花费=P*复制的长度\),要用单调栈来维护)

//#pragma comment (linker,"/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
//#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB emplace_back
//#define LL long long
#define pii pair<int,int>
#define MEM(x,y) memset(x,y,sizeof(x))
#define bug(x) cout<<"debug "#x" is "<<x<<endl;
#define FIO ios::sync_with_stdio(false);
#define ALL(x) x.begin(),x.end()
#define LOG 14
const int inf =0x3f3f3f3f;
const int maxn =4e5+7;
char s[maxn];
int N;
int P,Q;
int fa[LOG][maxn];
int dp[maxn];
int dis[maxn];
struct SAM{//下标从1开始,0作为保留位,用于做哨兵
    //private:
    struct node{
        int len,par,trans[27];
        //node(int parent,int length) :par(parent),len(length){ memset(trans,-1,sizeof(trans));}
    };
    node nd[maxn];
    int sz;
    inline void newnode(int parent,int length){node &n=nd[sz];n.par=parent,n.len=length;memset(n.trans,-1,sizeof(n.trans));++sz;};
    int last;
    //public:
    //vector<node> nd;
    SAM():last(1){sz=0;newnode(0,0);newnode(0,0);}
    void init(){last=1;sz=0;newnode(0,0);newnode(0,0);}
    void extend(const char c){
        register int p=last;
        newnode(1,nd[last].len+1);//新建状态,先让parent指向根(1)
        int np=sz-1;
        while(p!=0&&nd[p].trans[c]==-1){//如果没有边,且不为空,根也是要转移的
            nd[p].trans[c]=np;//他们都没有向np转移的边,直接连过去
            p=nd[p].par;//往parent走
        }
        if(p!=0){//如果p==0,直接就结束了,什么都不用做,否则节点p是第一个拥有转移c的状态,他的祖先都有转移c
            int q=nd[p].trans[c];//q是p转移后的状态
            if(nd[q].len==nd[p].len+1)nd[np].par=q;//len[q]是以前的最长串,len[p]+1是合并后的最长串,相等的话,不会影响,直接结束了,
            else{
                newnode(nd[q].par,nd[p].len+1);
                const int nq=sz-1;
                node &n=nd[nq];
                node &m=nd[q];
                for(register short i=25;i+1;--i) n.trans[i]=m.trans[i];
                nd[np].par=nd[q].par=nq;//改变parent树的形态
                while(nd[p].trans[c]==q){//一直往上面走
                    nd[p].trans[c]=nq;//所有向q连边的状态都连向nq
                    p=nd[p].par;
                }
            }
        }
        last=np;//最后的那个节点
    }
    int getdp(){
        memset(dis,inf,sizeof(int)*sz);
        register int top=1;
        dis[1]=0;
        dp[0]=0;
        fa[0][1]=1;
        for(register int i=sz-1;i>=2;--i)fa[0][i]=nd[i].par;
        for(register short k=1;k<LOG;++k)for(register int i=sz-1;i>=1;--i)fa[k][i]=fa[k-1][fa[k-1][i]];
        for(register int i=0;i<N;++i){
            top=nd[top].trans[s[i]-'a'];
            register const int ll=nd[top].len;
            dp[i+1]=dp[i]+P;
            register int j=-1,idx=top;
            for(register short k=LOG-1;k>=0;--k)
                if(fa[k][idx]!=1&&dis[fa[k][idx]]+nd[fa[k][idx]].len>ll)
                    idx=fa[k][idx];
            j=fa[0][idx];
            const int mx=dp[i+1-nd[j].len]+Q;
            if(j!=1&&dis[j]+nd[j].len<=ll&&dp[i+1]>mx)
                dp[i+1]=mx;
            dis[top]=ll;
            register int x=top;
            while(dis[nd[x].par]==inf){
                dis[nd[x].par]=ll;
                x=nd[x].par;
            }
        }
        return dp[N];
    }
}sam;
int main(){
    //freopen("02", "r", stdin);
    //freopen("02.txt", "w", stdout);
    while(~scanf("%s%d%d",s,&P,&Q)){
        sam.init();
        N=strlen(s);
        for(int i=0;i<N;++i)sam.extend(s[i]-'a');
        printf("%d\n",sam.getdp());
    }
    return 0;
}

/***
hdfajklshghghfklashfghghghghgasldifffddsuhsdfgahsufgajsdkhfasdfhdfasgdfsghghdhfjkasdfhasdfhsadghghlkfhaskdfhadfgalghghdfjhasghghghkdjfhaksf
2 1

out:87
***/
posted @ 2019-10-08 10:39 zhangxianlong 阅读(...) 评论(...) 编辑 收藏