DP套DP
该技巧通常用来求满足某个要求的元素数量,而对于要求的判定需要 DP 解决。
因此我们将判定 DP 的结果作为计数 DP 的状态来进行计数。
P4590 [TJOI2018] 游园会
题意:给你一个字符串 \(s\),求满足条件的字符串 \(t\) 的数量:\(\vert t\vert = n,\ \text{lcs}(s, t) = i,\ \text{"NOI"} \notin t\)。
对每个 \(i \in [0, \vert s\vert]\) 求出答案。\(\vert s\vert \le 15,\ n \le 10^3,\ \sum = \{\text{N},\text{O}, \text{I}\}\)。
设 \(f_{i, j}\) 表示 \(t\) 的前缀 \(i\) 和 \(s\) 的前缀 \(t\) 的 lcs(最长公共子序列):
要想得到 \(f_i\),我们只需知道 \(f_{i - 1}\) 和 \(t_i\),且每种方案唯一对应一组 \(f_{i - 1}, t_i\)。
把 \(f_{i}\) 压入状态减小复杂度。\(dp(i, f_i)\) 表示考虑了前 \(i\) 个字符,第 \(i\) 维 dp 数组为 \(f_i\) 的方案数,枚举 \(t_i\) 转移
合法的 \(f_{i}\) 实际很少,注意到 \(0\le f_{i, j} - f_{i, j - 1} \le 1\),每一种合法方案差分后能唯一对应一个长度为 \(\vert s\vert\) 的二进制数。
对于子串的限制,再记录一维 \(k\) 表示匹配到 "NOI" 的哪一位即可。
预处理每种 \(f_{i - 1}\) 以及 \(t_i\) 会转移到哪个 \(f_i\),时间复杂度 \(O(2^{\vert s\vert} n)\)。submission
CF578D LCS Again
题意:给定字符串 \(s\),求满足 \(\text{lcs}(s, t) = n - 1\) 的字符串数量。\(n \le 10^5\)。
继续延续上题做法不是很牛,复杂度高达 \(2^nn\)。
考虑哪些状态是一定无用的。\(\text{lcs}(i, j) \le \min(i, j)\),后缀 \(i, j\) 能匹配的最大长度是 \(\min(n - i, n - j)\)。
必须满足 \(\min(i, j) + \min(n - i, n - j) = n - \vert i - j\vert \ge n - 1\) 才可能对答案产生贡献。
需要考虑的状态一共就三个:\(f_{i, i - 1},\ f_{i, i},\ f_{i, i + 1}\)。
再注意到 \(f_{i, j} \ge \min(i, j) - 1\),否则也不能产生贡献。
设 \(dp_{i, 0/1, 0/1, 0/1}\) 表示 三项分别为 \(\{i - 2, i - 1\},\ \{i - 1, i\},\ \{i - 1, i\}\) 的方案数,复杂度 \(O(\vert \sum\vert n)\)。