Typewrite
\[ Time Limit: 1500 ms\quad Memory Limit: 262144 kB \]
题意
给出一个字符串 \(s\),现在你需要构造出这个字符串,你每次可以花费 \(q\) 在末尾加上任意一个字符或者花费 \(p\) 复制任意一段已经构造出来的子串到末尾,问最少需要花费多少。
思路
令 \(dp[i]\) 表示构造到第 \(i\) 位最少花费多少。
第一种情况,直接花费 \(q\) 添加在末尾,\(dp[r] = dp[r-1]+q\)
第二种情况,花费 \(p\) 复制一部分到末尾,设 \(s[l+1...r]\) 是被复制的串,那么此时需要满足 \(s[l+1...r] \in s[1...l]\),则 \(dp[r] = dp[l] + p\),此时的问题就是如何维护 \(l\)。
利用后缀自动机可以表示出所有子串的性质,\(pos\) 表示满足条件的 \(s[l+1...r]\) 所在后缀自动机上的节点位置。
每次插入 \(s[r]\) 时,就是从 \(s[l+1...r-1]\in s[1...l] \implies s[l+1...r] \in s[1...l]\) 的过程,所以要让 \(pos\) 节点往 \(s[r]\) 方向移动。如果能移动的话,直接将 \(pos\) 移动过去,否则就扩展这个自动机,将 \(s[l]\) 插入后在尝试能否移动。在这个过程中,不断维护 \(pos\) 在满足条件范围的节点上。
- 一个问题就是 \(pos\) 何时不能向 \(s[r]\) 方向移动,这种情况就是 \(pos\) 节点不存在 \(s[r]\) 这条边。
- 令一个问题就是如何维护 \(pos\) 在符合条件的范围内,我们知道 \(pos\) 节点包含了同样性质的长度从 \(len[fa[pos]]+1\) 到 \(len[pos]\) 内的子串,我们只要保证这里面最短的子串 \(len[fa[pos]]+1\) 不要太长,需要可以表示出后面那部分的串。在插入 \(s[r]\) 之前,此时只到 \(r-1\),需要保证 \(len[fa[pos]]+1 \leq (r-1)-(l+1)+1\),插入 \(s[r]\) 后,此时到了 \(r\),需要保证 \(len[fa[pos]]+1 \leq r-(l+1)+1\)
#include