版权声明:本文为博主原创文章,未经博主允许不得转载。 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]);
}
}