HDU6583 Typewriter
tags:后缀自动机
题意
一开始你有一个空串,你有两种操作,一是向这个串的末尾加上任意小写字母,代价为 \(p\), 二是向这个串的末尾加上这个串的任意一个子串,告诉你最终状态,让你求最少要多少代价才能变成最终状态
题解
用 \(f_i\) 表示变成 \(s[1,i]\) 的最小代价
那么对于第一种操作 \(f_i=f_{i-1}+p\)
对于第二种操作 \(f_i=f_{j-1}+q\), 其中, \(s[j,i]\) 在 \(s[1,j-1]\) 种出现过
第一种很好转移,第二种用后缀自动机维护,由于 \(j\) 是单调的,所以在 \(j\) 向右移时将 \(s_j\) 加入 SAM, 然后维护 \(s[l,r]\) 的节点即可,时间复杂度是 \(O(n)\) 的
#include<cstdio>
#include<cstring>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
template<typename T>inline void rd(T&x){int fl=0,ch;while(ch=getchar(),ch<48||57<ch)fl^=!(ch^45);x=(ch&15);while(ch=getchar(),47<ch&&ch<58)x=x*10+(ch&15);if(fl)x=-x;}
template<typename T>inline void pt(T x){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);}
template<typename T>inline void pt(T x,int ch){pt(x),putchar(ch);}
template<typename T>inline T max(const T&x,const T&y){return x<y?y:x;}
template<typename T>inline T min(const T&x,const T&y){return x<y?x:y;}
typedef long long ll;
const int N=200005;
int n,w1,w2,f[N];char s[N];ll ans;
struct SAM{
int cnt,lst,fa[N<<1],ch[N<<1][26],len[N<<1];
SAM(){cnt=lst=1;}
void clear(){
for(int i=1;i<=cnt;++i)fa[i]=len[i]=0,memset(ch[i],0,sizeof(ch[i]));
cnt=lst=1;
}
void extend(int c){
int p=lst,np=lst=++cnt;len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(len[p]+1==len[q])fa[np]=q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
}
}sam;
signed main(){
while(scanf("%s",s+1)!=EOF){
rd(w1),rd(w2);n=strlen(s+1);
sam.clear();int p=1,l=1;
for(int r=1;r<=n;++r){
f[r]=f[r-1]+w1;int c=s[r]-'a';
while((!sam.ch[p][c]||r-l+1>l-1)&&l<=r){
sam.extend(s[l++]-'a');
while(p&&sam.len[sam.fa[p]]>=r-l)p=sam.fa[p];
if(!p)p=1;
}
p=sam.ch[p][c];
while(p&&sam.len[sam.fa[p]]>=r-l+1)p=sam.fa[p];
if(!p)p=1;
if(l<=r){
f[r]=min(f[r],f[l-1]+w2);
}
}
pt(f[n],'\n');
}
return 0;
}