【题目描述】
有一个空串,每次可以花费 $a$ 的代价在后面添加一个字母,或花费 $b imes lvert S vert$ 的代价将该串复制一遍放到前面。其中 $lvert S vert$ 为当前字符串长度。
现给定一个字符串 $S$,求构造 $S$ 的最小代价。
【输入格式】
第一行一个字符串 $S$。
第二行两个正整数 $a,b$。
【输出格式】
一行一个正整数表示最小代价。
【样例】
样例输入
aab
3 2
样例输出
8
【数据范围与提示】
字符串长度不超过 $10^6$,$a,b leq 10^5$。
【题解】
显然直接 $dp$ 即可。
$$f_i=min(f_{i-1}+a,[i mod 2==0][str(1,frac{i}{2})==str(frac{i}{2}+1,i)](f_{frac{i}{2}}+b imes frac{i}{2}))$$
判断字符串相等可用 $exkmp$ $hash$。
【代码】
#include<bits/stdc++.h> typedef unsigned long long Hash; const Hash HASH=13131313; Hash Pow[1000010],hsh[1000010]; long long f[1000010],a,b; char S[1000010]; inline Hash check ( int l,int r ) { return hsh[r]-hsh[l-1]*Pow[r-l+1]; } signed main() { scanf("%s %lld%lld",S+1,&a,&b); int n=strlen(S+1);Pow[0]=1; for ( int i=1;i<=n;i++ ) Pow[i]=Pow[i-1]*HASH; for ( int i=1;i<=n;i++ ) hsh[i]=hsh[i-1]*HASH+S[i]; for ( int i=1;i<=n;i++ ) { f[i]=f[i-1]+a; if ( !(i&1) and check(1,i>>1)==check((i>>1)+1,i) ) f[i]=std::min(f[i],f[i/2]+1LL*b*(i/2)); } return !printf("%lld ",f[n]); }