给定一个长度为 \(n\) 的字符串, 要求将字符串分割为若干段, 每一段要么是一个字符要么是前面几段的并的子串.c++
若是某一段是一个单独字符, 则产生 \(a\) 的开销.curl
若是是前几段的并的子串, 则产生 \(b\) 的开销.优化
若是知足两个条件, 则能够在 \(a,b\) 中任选一个开销.url
求划分的最小开销.spa
\(n\le 5\times 10^6\), 字符集大小 \(\Sigma\le 7\).3d
冷静分析一下发现是沙雕题code
然而题目说不卡常其实是真的卡常...严格 \(O(n)\) 并不必定能跑过...blog
咱们发现这个沙雕题最后一个串的开销和前面的划分方案无关, 果断想到DP. 设 \(dp_i\) 表示长度为 \(i\) 的前缀的最小划分代价.排序
而后有个显然的性质, 就是若是有一个后缀知足它在前面出现过, 那么这个后缀的全部后缀一样都知足. 因此不难想到对于当前DP前缀找到知足该性质的最长的后缀, 而后在这个后缀的全部后缀中找最小DP值更新.队列
不难发现这个过程实际上能够用后缀自动机解决. 对于每一个状态都维护一下 \(right\) 集合中的最小值, 也就是当前状态所表明字符串的第一次出现的右端点的位置. 这样咱们就能够直接知道当先后缀是否在前面出现过了.
不难发现这个后缀的左端点位置也是单调的, 因此不合法暴力跳就能够 \(O(n)\) 了.
按照刚刚的讨论咱们须要把全部合法的后缀的 \(dp\) 值取 \(\min\), 须要单调队列来维护. 可是实际上不难发现 \(dp\) 值是单调不降的, 因此直接取最长合法后缀的左端点处的 \(dp\) 值就能够了.
可是要想A这题还得加点优化. 一个是 \(right\) 集合的最小值的维护, 并不须要给后缀自动机结点基数排序. 由于每次插入的新点的 \(right\) 集合中的值其实是单调递增的, 并且若是在原来自动机上某个点在另外一个点的子树中的话扩展后必定还在那个点的子树里. 因此能够边构造边算.
其次是咱们并不须要先构造出整个SAM而后再跑, 咱们彻底能够边构造边计算最长知足条件的后缀. 由于后面的字符串并不会影响前面已有的信息.
加了这两个优化才卡时限过的...这可真蠢.
(不难分析获得不加两个优化+单调队列实际上也是严格 \(O(n)\) 的时间复杂度)
#include <bits/stdc++.h> const int MAXN=1e7+10; typedef long long intEx; int n; int a; int b; int cnt=1; int last=1; int root=1; int s[MAXN]; int buc[MAXN]; int len[MAXN]; int prt[MAXN]; int minr[MAXN]; char str[MAXN]; intEx dp[MAXN]; int chd[MAXN][7]; void Extend(char); int main(){ scanf("%d%d%d",&n,&a,&b); scanf("%s",str+1); int cur=root,curlen=0; for(int i=1;i<=n;i++){ Extend(str[i]); int p=str[i]-'a'; while(!chd[cur][p]){ cur=prt[cur]; curlen=len[cur]; } ++curlen; cur=chd[cur][p]; while(cur!=root&&minr[cur]>i-curlen){ if(i-minr[cur]>len[prt[cur]]) curlen=i-minr[cur]; else{ cur=prt[cur]; curlen=len[cur]; } } dp[i]=dp[i-1]+a; if(curlen) dp[i]=std::min(dp[i],dp[i-curlen]+b); } printf("%lld\n",dp[n]); return 0; } void Extend(char ch){ int x=ch-'a'; int p=last; int np=++cnt; last=np; minr[np]=len[np]=len[p]+1; while(p&&!chd[p][x]) chd[p][x]=np,p=prt[p]; if(!p) prt[np]=root; else{ int q=chd[p][x]; if(len[q]==len[p]+1) prt[np]=q; else{ int nq=++cnt; memcpy(chd[nq],chd[q],sizeof(chd[q])); len[nq]=len[p]+1; prt[nq]=prt[q]; prt[q]=nq; prt[np]=nq; minr[nq]=minr[q]; while(p&&chd[p][x]==q) chd[p][x]=nq,p=prt[p]; } } }