FFT在字符串匹配中的应用
对于一类字符串匹配问题:
给定长度分别为 \(m,n\) 的字符串 \(A,B\),给出两个相同长度的字符串匹配的定义,要求找出 \(A\) 在 \(B\) 中所有匹配的位置。
可能能用如下方式解决:
定义匹配函数 \(C(a,b)\),使得完全匹配函数 \(P(i)=\sum_{j=0}^{m-1}C(A_j,B_{i+j})\) 等于 \(0\) 当且仅当 \(A\) 和 \(B[i..i+m-1]\) 匹配。
然后,我们将 \(C(a,b)\) 拆成若干个有关 \(a,b\) 的乘积,即 \(C(a,b)=\sum_{k=1}^K f_k(a)g_k(b)\)。然后我们对每个 \(k\) 计算它对 \(P(i)\) 的贡献,即计算 \(P_k(i)=\sum_{j=0}^{m-1}f_k(A_j)g_k(B_{i+j})\)。这是减法卷积,可以优化至 \(O(n\log n)\)。
总时间复杂度 \(O(Kn\log n)\),一般来说 \(K\) 是常数。
举几个例子吧:
-
一般的字符串匹配问题。
一个想法是设 \(C(a,b)=a-b\),但这只对单个字符生效,对 \(P(i)\) 是不一定生效的,比如 \(\texttt{"ab"}\) 和 \(\texttt{"ba"}\)。
正确的做法应该是设 \(C(a,b)=(a-b)^2\),这样两个字符串一旦出现某个位置不同,\(P(i)\) 就一定大于 \(0\)。
-
带通配符 \(\texttt{\#}\) 的字符串匹配问题。
上一题的扩展,直接设 \(C(a,b)=[a\neq \texttt{\#}][b\neq \texttt{\#}](a-b)^2\) 即可。