题意就是让你拼字符串
在后面添加任意一个字符的代价是p,将原串的子串复制到后面的代价是q,
怎么做呢
设DP[i]代表着拼成前i个字符的 最小花费
我们假设 在 j--i 的字符串 是 0--j-1部分的子串
那么有dp[i]=min( dp[i-1]+p , dp[j-1]+q )
利用后缀自动机维护 j 的位置
cur 是当前0--j-1内子串的状态
每次都对第i个字符进行匹配,如果匹配到,就跳转到下一个状态,继续匹配第i+1个字符
如果匹配失败,那就将第j个字符加入自动机,同时j++
因为随着j的增大,j--i的子串长度会变短,前缀会一直去掉,所以我们就取跳到fa[cur],改变匹配的位置
保证了最优解 注意一些 i-j i-j+1的细节
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5; struct SAM {int trans[maxn<<1][26],slink[maxn<<1],maxlen[maxn<<1]; int last,now,root,len; void newnode(intv) { maxlen[++now]=v; }void extend(intc) { newnode(maxlen[last]+1); int p=last,np=now; while(p&&!trans[p][c]) { trans[p][c]=np; p=slink[p]; } if(!p) slink[np]=root; else { intq=trans[p][c]; if(maxlen[p]+1!=maxlen[q]) { newnode(maxlen[p]+1); int nq=now; memcpy(trans[nq],trans[q],sizeof(trans[q])); slink[nq]=slink[q]; slink[q]=slink[np]=nq; while(p&&trans[p][c]==q) { trans[p][c]=nq; p=slink[p]; } }else slink[np]=q; } last=np; }void build() { root=last=now=1; for(inti=0;i<(maxn<<1);i++) memset(trans[i],0,sizeof(trans[i])); memset(slink,0,sizeof(slink)); memset(maxlen,0,sizeof(maxlen)); } }sam; char s[maxn]; ll dp[maxn]; int main() { while(scanf("%s",s)!=EOF) { intp,q; scanf("%d%d",&p,&q); sam.build(); sam.len=strlen(s); int cur=1; dp[0]=p; int j=1; sam.extend(s[0]-'a'); for(inti=1;i<sam.len;i++) { dp[i]=dp[i-1]+p; while((!sam.trans[cur][s[i]-'a']||(i-j+1)>j)) { sam.extend(s[j]-'a');j++; while(cur&&sam.maxlen[sam.slink[cur]]>=(i-j)) { cur=sam.slink[cur]; } if(!cur) cur=1; } cur=sam.trans[cur][s[i]-'a']; while(cur&&sam.maxlen[sam.slink[cur]]>=(i-j+1)) { cur=sam.slink[cur]; } if(!cur) cur=1; if(i>=j) dp[i]=min(dp[i],dp[j-1]+q); } printf("%lld\n",dp[sam.len-1]); }return 0; }