#3314. constructive
题目描述
JohnCheng最近沉迷几何,导致走火入魔,于是打算搞下string来缓解压力。他有一张草稿纸,最开始什么都没有(可以视为有一个空串)。每一步他可以花费 $a$ 时间在当前字符串后面加入任意一个字符,也可以花费 $b \times |S|$ (S为当前字符串)的时间把S抄一遍放在S前面。
旁边的Worldwide_D正苦于一道字符串毒瘤题,他获得了一个数据点,数据点里有一个长度不大于 $10^6$ 的字符串。他把字符串发给JohnCheng,问他至少能在多长时间内构造出这个字符串。
JohnCheng:水题一道。然后开始睡觉,并把问题交给了你。
数据范围
$∣S∣≤10 ^6 ,1≤b<a≤10 ^5$
题解
考虑 $dp$ , $f_i$ 表示构造好了前 $i$ 个字符的最小代价,则首先 $f_i=f_{i-1}+a$
然后如果 $i$ 是偶数并且 $[1.\frac{i}{2}]$ 和 $[\frac{i}{2}+1,i]$ 的字符串相同的话,则有 $f_i=min(f_i,f_{\frac{i}{2}}+b \times \frac{i}{2})$ ,判断相同就用哈希判断
效率: $O(|S|)$
代码
#include <bits/stdc++.h> #define LL long long #define U unsigned LL using namespace std; const int N=1e6+5; const U K=793999; int a,b,n;char s[N]; LL f[N];U h[N],k[N]; U H(int l,int r){ return h[r]-h[l-1]*k[r-l+1]; } int main(){ scanf("%s%d%d",s+1,&a,&b); n=strlen(s+1);k[0]=1; for (int i=1;i<=n;i++) k[i]=k[i-1]*K,h[i]=h[i-1]*K+s[i]; for (int i=1;i<=n;i++){ f[i]=f[i-1]+a; if (!(i&1) && H(1,i>>1)==H((i>>1)+1,i)) f[i]=min(f[i>>1]+1ll*b*(i>>1),f[i]); } return printf("%lld\n",f[n]),0; }