HDU-5470 Typewriter (后缀自动机优化dp)
HDU-5470 Typewriter (后缀自动机优化dp)
这个\(dp\)有两种转移
1.\(dp[i-1]+cost[s[i]] \rightarrow dp[i]\)
2.$ dp[j]+(i-j) \cdot A+2 \cdot B \rightarrow dp[i] $ ( $s_{j+1,i} $ 是 \(s_{1,j}\) 的一个子串)
第一种转移不必多说
对于第二种转移,合法的下标\(j\)一定是到\(i-1\)的一段连续区间
设临界的下标\(j\)为\(x\),找到\(S_{x,i}\)对应的状态\(st\),那么\(min\{endpos_{st}\} \leq x\)
我们在转移的同时,不断匹配\(S_{1,i}\)对应的状态\(p\),那么\(S_{x,i}\)对应的状态就一定是\(p\)在\(parent/link\)树上的一个祖先
暴力一点的话,可以通过 树上倍增+线段树/树状数组 来实现
#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
#define pb push_back
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10;
int n,A,B;
char S[N];
int Cost[30];
ll dp[N],temp[N];
int fa[20][N];
int vis[N];
struct I_Hate_It{// 线段树
ll s[N<<1];
int bit;
void Init(){
bit=1;
while(bit<=n+2) bit<<=1;
rep(i,1,bit+n+1) s[i]=1e18;
}
void Upd(int p,ll x){
p++;
p+=bit,s[p]=x;
while(p>1) p>>=1,s[p]=min(s[p<<1],s[p<<1|1]);
}
ll Que(int l,int r) {
l++,r++;
if(l==r) return s[l+bit];
ll res=1e18;
for(l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1) {
if(~l&1) cmin(res,s[l^1]);
if(r&1) cmin(res,s[r^1]);
}
return res;
}
}Tree;
int trans[N][26],link[N],len[N],stcnt,lst,First[N];
void Init(){
link[0]=-1,len[0]=0;
rep(i,0,stcnt) {
vis[i]=0;
rep(j,0,25) trans[i][j]=0;
}
lst=stcnt=0;
}
void Extend(int c) {
int cur=++stcnt,p=lst;
First[cur]=len[cur]=len[p]+1;
while(~p && !trans[p][c]) trans[p][c]=cur,p=link[p];
if(p==-1) link[cur]=0;
else {
int q=trans[p][c];
if(len[q]==len[p]+1) link[cur]=q;
else {
int clone=++stcnt;
memcpy(trans[clone],trans[q],104);
First[clone]=First[q];
len[clone]=len[p]+1,link[clone]=link[q];
while(~p && trans[p][c]==q) trans[p][c]=clone,p=link[p];
link[cur]=link[q]=clone;
}
}
lst=cur;
}
int main(){
rep(kase,1,rd()) {
scanf("%s",S+1),n=strlen(S+1);
dp[0]=0;
Init(),Tree.Init();
rep(i,1,n) Extend(S[i]-'a');
rep(i,0,25) Cost[i]=rd();
A=rd(),B=rd();
int p=0;
fa[0][0]=0; rep(i,1,stcnt) fa[0][i]=link[i];
rep(i,1,18) rep(j,1,stcnt) fa[i][j]=fa[i-1][fa[i-1][j]];
rep(i,1,n) {
dp[i]=1e18;
p=trans[p][S[i]-'a'];
int now=p;
if(now && First[now]<i-len[link[now]]) cmin(dp[i],Tree.Que(First[now],i-1));
else {
drep(j,17,0) if(fa[j][now] && First[fa[j][now]]>=i-len[link[fa[j][now]]])
now=fa[j][now];
now=fa[0][now];
if(now && max(i-len[now],First[now])<i-len[link[now]])
cmin(dp[i],Tree.Que(max(i-len[now],First[now]),i-1));//倍增找到合法子串
}
dp[i]+=1ll*i*A+B*2;
cmin(dp[i],dp[i-1]+Cost[S[i]-'a']);
Tree.Upd(i,dp[i]-1ll*i*A);
}
printf("Case #%d: %lld\n",kase,dp[n]);
}
}