dp套dp 学习笔记
定义
在 dp 时,我们经常会设计一个状态,接着给一些状态的初始值,然后让 dp 从某个状态向某个状态转移。最终计算答案。
然后我们吧 dp 状态看成结点,转移看成边,我们发现他就是 DFA 呀。
所以我们可以直接记录在若干步之后在 DFA 上的 \(u\) 号节点的方案数。
所以,在 dp套dp 里面,我们就将内层 dp 的结果作为外层 dp 的状态进行 dp。
例题
举个例子,加深理解。
[TJOI2018] 游园会
- 求长度为 \(n\),字符集为 \(\left\{ \mathrm{N},\mathrm O,\mathrm I\right\}\),与给定字符串 \(S\) 的 \(\mathrm {LCS}\) 为 \(len\) 的字符串数量。
- \(|S| \leq 15\),\(n \leq 10^3\)
我们显然有 \(f_{i,j}=\max(f_{i-1,j},f_{i,j-1},f_{i-1,j-1}+(S_i==T_j))\) 的求 \(\mathrm {LCS}\) 的 dp。
第一部分最后一句中,我们提到外层 dp 的状态就是内层 dp 的结果,于是一个最暴力的想法就是记录所有 LCS 作为状态。
此时,我们要记录的是长度为 i,和 \(S\) 的 \(\mathrm {LCS}\) 为某个数组的字符串数量。
然而我们发现,如果我们在某个字符串后面加一个新的字符,只会新生成一行 LCS,而这一行 LCS 完全通过上一行生成!
于是我们只要记录 LCS 最后一行为某个数组的字符串数量了。
然后我们还发现 \(LCS_{x+1,y}-LCS_{x,y}\) 只能是 0 或者 1,于是我们还能差分最后一行得到一个 01 字符串并再次状压。
对了,然后还要记录是否以 N, NO 为后缀。
然后就可以得到优美的代码了。