LOADING

加载过慢请开启缓存 浏览器默认开启

使用 FFT/NTT 进行字符串匹配

我们一般进行字符串匹配,有两种方法:KMP 和 Hash。
我们这里介绍一种新的方法,FFT。

前置知识:

  • FFT/NTT

不会的可以看看我的博客?这里


我们现在有两个由小写英文字母组成的字符串 \(A\)\(B\),现在 \(A\) 串是模式串,我们在 \(B\) 中进行匹配。
\(A\) 的长度为 \(m\)\(B\) 的长度为 \(n\)。) 常见的做法是 KMP,它可以在 \(O(n+m)\) 的时间复杂度下完成这么一个问题,非常优秀。
但是我们现在的串中含有通配符 *,思考一下,会发现 KMP 是无法解决的。

普通的字符串匹配

我们定义一种差异函数 \(f(x,y)\),表示 \(A_x\)\(B_y\) 之间的“距离”。也就是对应的 ASCII 码的差值。很显然,若 \(f\) 函数的值为 \(0\),说明这两个位置是同一个字符。
那么我们对于 \(B\) 上的某个长度为 \(m\) 的子串,可以定义一个函数 \(P(x)\),表示以 \(B_{x}\) 结尾的长度为 \(m\) 的子串与 \(A\) 的差异值。那么有 \(P_{x}=\sum_{i=x-m+1}^{x}f(i-x+m-1,i)\)。也很显然,\(P(x)\) 如果为 \(0\),说明 \(B\) 的子串 \(B[x-m+1,x]\)\(A\) 是完全匹配的。

但是我们会发现一个问题。
对于串 AB 和 串 BA,根据我们的函数计算出来,差异值为 \(0\)
为什么?
因为正负性。
取消正负性有两种方法,一是绝对值,二是平方。显然绝对值是不好处理的。所以我们平方一下。

\[ P_{x}=\sum_{i=x-m+1}^{x}[f(i-x+m-1,i)]^2 \]

\(f\) 函数展开,可以得到:

\[ P_{x}=\sum_{i=x-m+1}^{x}(A_{i-x+m-1}-B_{i})^2 \]

如果我们直接将平方展开,会变成这样:

\[ P_{x}=\sum_{i=x-m+1}^{x}\left(A_{i-x+m-1}^2+B_{i}^2-2A_{i-x+m-1}B_{i}\right) \]

看不出来什么东西。

试试将求和符号丢进每一项看看。

\[ P_{x}=\sum_{i=x-m+1}^{x}A_{i-x+m-1}^2+\sum_{i=x-m+1}^{x}B_{i}^2-2\sum_{i=x-m+1}^{x}A_{i-x+m-1}B_{i} \]

发现第一项是一个定值,也就是 \(\sum_{i=0}^{m-1}A_{i}^{2}\)
第二项对于 \(B_{i}^{2}\) 进行一个前缀和预处理,也可以快速算出。
第三项看着有点麻烦。我们发现 \(i-x+m-1-i\) 是一个定值 \(-x+m-1\),而如果我们能将 差为定值,变成 和为定值,那就是一个卷积的形式了!
怎么变成 和为定值 呢?很简单,我们将 \(A\) 串进行翻转操作得到一个新的串 \(S\),易得 \(S_{i}=A_{m-i-1},A_{i}=S_{m-i-1}\),于是最后一项就变成了 \(S_{m-(i-x+m-1)-1}B_{i}=S_{x-i}B_{i}\),这下和就是一个定值 \(x\) 了!我们便可以使用 FFT/NTT 来加速这一过程。

至此,我们使用 FFT/NTT,在 \(O(n\log n)\) 的时间复杂度下完成了普通的字符串匹配。

带通配符的字符串匹配

这才是我们要讲的重点,也就是这道题:
Luogu P4173 残缺的字符串

这道题中,我们的字符串 \(A\)\(B\) 都多了一种字符:通配符。此时我们先前的差异函数 \(f(x,y)\) 显然就存在问题了,因为 * 的 ASCII 值是 \(42\),而 \(a\) 的 ASCII 值则是 \(97\)。相减显然会出现问题。
我们知道,当 \(f\) 函数值为 \(0\) 时表示相同,那么我们不妨令 * 对应 \(0\) 这个值,其他的 ab 则表示 \(1\)\(26\),那么我们新的差异函数 \(f(x,y)\) 就应该等于 \(A_{x}B_{y}(A_{x}-B_{y})^2\)
同理:

\[ P_{x}=\sum_{i=x-m+1}^{x}A_{i-x+m-1}B_{i}(A_{i-x+m-1}-B_{i})^2 \]

我们不妨还是将 \(A\) 串翻转一下得到串 \(S\),并且将式子展开化简。

\[ \begin{aligned} P_{x}&=\sum_{i=x-m+1}^{x}S_{x-i}B_{i}(S_{x-i}-B_{i})^2\\ &=\sum_{i=x-m+1}^{x}S_{x-i}^3B_{i}+S_{x-i}B_{i}^3-2S_{x-i}^2B_{i}^2\\ &=\sum_{i=x-m+1}^{x}S_{x-i}^3B_{i}+\sum_{i=x-m+1}^{x}S_{x-i}B_{i}^3-2\sum_{i=x-m+1}^{x}S_{x-i}^2B_{i}^2\\ \end{aligned} \]

不难发现每一项都是卷积的形式,直接 FFT/NTT 做就行了。
那么就结束啦!

使用 FFT 的参考代码:

使用 NTT 的参考代码(from:Untitled_unrevised

最后放一道课后习题:CF528D Fuzzy Search