题目链接:HDU - 6583
显然可以dp, dp[i] 为以 i 结尾的最小花费。
dp[i]=min(dp[i-1]+p,dp[j]+q)保证 str(j+1,i) 为 str(1,j) 的子串即可。
显然这个花费是单调递增的。由子串的性质可以证明。
所以我们枚举的时候,可以用 尺取 + SAM 求出可以转移的最远的 j 。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=4e5+10;
char str[N]; int n,p,q,now,pos; long long dp[N];
struct SAM{
int cnt=1,last=1,sz[N];
struct node{int ch[26],len,fa;}d[N];
inline void init(){
for(int i=1;i<=cnt;i++) d[i].fa=d[i].len=0,memset(d[i].ch,0,sizeof d[i].ch);
cnt=last=1;
}
inline void insert(int c){
int p=last,np=last=++cnt; sz[cnt]=1;
d[np].len=d[p].len+1;
for(;p&&!d[p].ch[c];p=d[p].fa) d[p].ch[c]=np;
if(!p) d[np].fa=1;
else{
int q=d[p].ch[c];
if(d[q].len==d[p].len+1) d[np].fa=q;
else{
int nq=++cnt;
d[nq]=d[q],d[nq].len=d[p].len+1,d[q].fa=d[np].fa=nq;
for(;p&&d[p].ch[c]==q;p=d[p].fa) d[p].ch[c]=nq;
}
}
}
}sam;
inline void solve(){
now=1,pos=1; n=strlen(str+1); sam.init();
for(int i=1,k;i<=n;i++){
dp[i]=dp[i-1]+p; k=str[i]-'a';
while((!sam.d[now].ch[k]||i-pos+1>pos-1)&&pos<=i){
sam.insert(str[pos++]-'a');
while(now&&sam.d[sam.d[now].fa].len>=i-pos) now=sam.d[now].fa;
if(!now) now=1;
}
now=sam.d[now].ch[k];
while(now&&sam.d[sam.d[now].fa].len>=i-pos+1) now=sam.d[now].fa;
if(!now) now=1;
if(pos<=i) dp[i]=min(dp[i],dp[pos-1]+q);
}
printf("%lld\n",dp[n]);
}
signed main(){
while(~scanf("%s %d %d",str+1,&p,&q)) solve();
return 0;
}