HDU5470 Typewriter

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/81259938

题面

题意

给出一个字符串,现在你要打印它,你有两种操作:
1.在已打印字符串后面打印一个字符,代价为该字母的代价。
2.选择已打印的字符串的一个子串s,在后面复制粘贴,代价为|S|*A+2*B。

做法

第一个操作很好处理,难点在于第二个操作。
首先考虑dp[i]表示打印前i个字母所需要的代价,这样dp[i]=min(dp[i-1]+need[i],dp[j]+(i-j)*A+2*B),其中j+1~i是1~j的子串。
为了维护1~i中有哪些j符合条件,我们对1~j内的字符建一个后缀自动机,随着dp的进行向其中加入字符,那么我们就可以用它来动态维护j后面最远的i,使j+1~i是1~j的子串,所以对于j+1~i的所有点都可以用dp[j]+(i-j)*A+2*B来更新,所以我们可以把它加入一个单调队列,当此时更新的点大于i,则将它pop,然后O(n)扫一遍即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define INF 0x3f3f3f3f3f3f3f3f
#define N 100100
using namespace std;

ll T,TT,n,tt,last,need[30],now,dp[N],pos,a,b,pp;
char str[N];
struct Node
{
    ll to[26],pa,len;
    Node()
    {
        pa=-1;
        memset(to,-1,sizeof(to));
    }
    void init()
    {
        len=0;
        pa=-1;
        memset(to,-1,sizeof(to));
    }
}node[N<<1];
struct Dq
{
    ll top,tail;
    P num[N];
    void init()
    {
        tail=0,top=1;
    }
    void pop()
    {
        for(;top<=tail&&num[top].se<=pos;top++);
    }
    void push(P u)
    {
        for(;top<=tail&&u.fi<=num[tail].fi;tail--);
        num[++tail]=u;
    }
    ll front()
    {
        if(top>tail) return INF;
        return num[top].fi-(n-pos)*a;
    }
};
Dq dq;

inline void add(ll u)
{
    ll np=++tt,p=last,q,nq;
    node[np].len=node[p].len+1;
    last=np;
    for(;p!=-1&&node[p].to[u]==-1;p=node[p].pa) node[p].to[u]=np;
    if(p==-1)
    {
        node[np].pa=0;
    }
    else
    {
        q=node[p].to[u];
        if(node[q].len==node[p].len+1)
        {
            node[np].pa=q;
        }
        else
        {
            nq=++tt;
            node[nq].pa=node[q].pa;
            memcpy(node[nq].to,node[q].to,sizeof(node[q].to));
            node[nq].len=node[p].len+1;
            node[np].pa=node[q].pa=nq;
            for(;p!=-1&&node[p].to[u]==q;p=node[p].pa) node[p].to[u]=nq;
        }
    }
}

int main()
{
    ll i,j;
    cin>>T;
    while(T--)
    {
        for(i=0;i<=tt;i++) node[i].init();
        now=last=tt=0;
        dq.init();
        scanf("%s",str+1);
        n=strlen(str+1);
        for(i=0;i<26;i++) scanf("%lld",&need[i]);
        scanf("%lld%lld",&a,&b);
        pp=0;
        for(pos=1,j=2;pos<=n;pos++)
        {
            dq.pop();
            if(pp) pp--;
            dp[pos]=min(dp[pos-1]+need[str[pos]-'a'],dq.front());
            add(str[pos]-'a');
            for(;now&&pp<=node[node[now].pa].len;) now=node[now].pa;
            if(!now) j=pos+1;
            for(;j<=n&&node[now].to[str[j]-'a']!=-1;now=node[now].to[str[j]-'a'],j++,pp++);
            dq.push(mp(dp[pos]+2*b+(n-pos)*a,j));
        }
        printf("Case #%lld: %lld\n",++TT,dp[n]);
    }
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/81259938
hdu