问题
给定原串 $s$ 与模式串 $t$。其中模式串可能含有一些 ? 字符(而原串没有),问号可以与任何字符匹配。问模式串与 $s$ 中哪些位置匹配?
形式化的:
我们称两个字符串相等,当且仅当:
- 它们长度相等
- 对于任意一个位置 $i$($0 \leq i < \mathrm{len}$,$\mathrm{len}$ 是两个字符串的长度),两个字符串在这个位置上的对应字符相等,或至少一个字符串在这个位置上的对应字符是问号。
现在的问题是,求出所有的 $0 \leq i \leq \operatorname{len}(s) - \operatorname{len}(t)$ 满足 $s$ 从 $i$ 开始(包含 $i$),长度为 $\operatorname{len}(t)$ 的子串与 $t$ 相等。
下设 $n := \operatorname{len}(s), \; m := \operatorname{len}(t)$。
bitset 做法
bitset 能够简单高效的解决这个问题,复杂度为 $O(nm/w)$。
具体的:对于每种字符 $c$,我们维护一个长度为 $n$ 的 bitset。bitset 的下标为 $i$ 的位置为 true 当且仅当 $s_i = c$。
注意到,假如 $i$ 是一个匹配的起始点,这意味着 $s_{i+j} = t_j$ 对所有 $0 \leq j < m$ 的 $j$ 成立。反过来:
一个匹配的起始点 $i$,必须在一个 $s_k = t_j$ 的位置 $k$ 前面 $j$ 格。
我们就可以遍历 $t$ 中所有字符,然后找到当前遍历到的字符在 $s$ 中的所有出现位置,它们向前平移 $j$(设 $j$ 是当前遍历到的下标)就是所有可能的匹配位置。那么我们用 bitset 的右移和逻辑与处理就行了。
通配符怎么办?通配符毫无约束效力,直接什么都不做就是对的。
简单的示例代码:
| |
巨大字符集
假如字符集不再是小写字母,而可能是任意整数,怎么做呢?
其实很简单。首先离散化所有字符。
然后还是照搬套路,但这次我们不再按位置枚举,而是按 $t$ 中不同的字符种类枚举,对于每种字符,我们遍历一次 $s$,构建出现位置的 bitset,并遍历它在 $t$ 中出现的每个位置,分别进行对应的移位和与即可。
时间复杂度不变。空间复杂度线性。
FFT/NTT
也可以解决本题,复杂度是更优秀的 $O(n \log n)$,但是太难了,不讲。