【题解】「CQOI2014」通配符匹配
【题解】「CQOI2014」通配符匹配
https://www.luogu.com.cn/problem/P3167
令 \(s\) 为模式串,\(t\) 为文本串。
首先有一个显然的的 dp 是,\(f_{i,j}\) 表示模式串的前 \(i\) 个和文本串的前 \(j\) 个是否匹配。
显然 \(O(n^2)\) 是过不了的。
Motivation: 注意到题目限定了通配符不多于 \(10\) 个,而在模式串里面如果不包含通配符其实是可以打包处理的。
所以我们考虑修改 dp 的定义为 \(f_{i,j}\) 表示模式串的前 \(i\) 个 通配符 和文本串的前 \(j\) 个字符是否匹配。
这样的话,转移就变成了:设当前这一段的长度为 \(l\),通配符为 \(c\),下标为 \(p\)。
\[f_{i,j}=\begin{cases}
f_{i-1,j-l} & \text{ if } c=\mathsf{?}\wedge s[p-l+1\cdots p-1]=t[j-l+1\cdots j-1] \\
f_{i-1,k}(0\le k\le j-l+1) & \text{ if } c=\mathsf{*}\wedge s[p-l+1\cdots p-1]=t[k+1\cdots k+l-1] \\
\end{cases}
\]
但是现在发现一个新问题,第二种转移需要枚举前缀,这会使我们的算法退化到 \(O(n^2)\)。
我们不考虑填表法,而考虑刷表法。
前者是根据根据一定限制条件来找可能造成贡献的 dp 值,而后者是找出可以对那些 dp 值造成贡献。
(但是两种各有所长,一般二者皆可,但是某些题目里面只能用其中的一种。
考虑刷表法。转移改写成:
\[f_{i,j}\to\begin{cases}
f_{i+1,j+l} & \text{ if } c=\mathsf{?}\wedge s[p-l+1\cdots p-1]=t[j+1\cdots j+l-1] \\
f_{i-1,k}(j+l-1\le k\le |t|) & \text{ if } c=\mathsf{*}\wedge s[p-l+1\cdots p-1]=t[j+1\cdots j+l-1] \\
\end{cases}
\]
我们注意到,现在第二种转移虽然还是要枚举 \(k\),但是每次需要判断是否匹配的子串是相同的,所以我们可以直接打个标记,最后统一更新后缀就好了。