小科技之卷积解决字符串匹配问题

小科技之卷积解决字符串匹配问题

OI中有各种字符串匹配问题,常见的有单模式串匹配、多模式串匹配和子串匹配等,一般可以用KMP、AC自动机、SAM解决。如果涉及到其他形式的单模式串匹配,这些传统的方式很不好解决,有没有更灵活的处理方法或者通法呢?

答案是有的,而且就是耳熟能详的FFT/NTT。

我们先考虑最基础的单模式串匹配,求长为m的字符串T在长为n的字符串S里出现了多少次,只不过我们要用卷积来实现。具体的,我们设\(C(i,j)\)表示\((s_i-t_j)\),即这两位是否相等,相等就为0,如果匹配了,那么自然有\(\sum\limits_{i=0}^{m-1}C(x-m+i+1,i)=0\),但是这样有反例,例如\(ab\)\(ba\)都可以和\(ab\)匹配,因此我们改成\(\sum\limits_{i=0}^{m-1}\left|C(x-m+i+1,i)\right|=0\)才行。但是我们维护这个也是\(O(m)\)的,所以我们考虑给这一项平方,再设\(ans(x)=\sum\limits_{i=0}^{m-1}C(x-m+i+1,i)^2=\sum\limits_{i=0}^{m-1}(s_{x-m+i+1}-t_{i})^2\),我们暴力拆柿子,得:

\[ans(x)=\sum\limits_{i=0}^{m-1}s^2_{x-m+i+1}-2\times s_{x-m+i+1}\times t_{i}+t^2_{i}=\sum\limits_{i=0}^{m-1}s^2_{x-m+i+1}+\sum\limits_{i=1}^m t^2_i-\sum\limits_{i=0}^{m-1}2\times s_{x-m+i+1}\times t_i \]

这时前两项可以预处理后\(O(1)\)算,问题就在于怎么求最后一项。我们发现这个形式不好看,于是我们把T翻转,即\(t_i\)变成\(t_{m-i-1}\),那么就变成了

\[\sum\limits_{i=0}^{m-1}2\times s_{x-m+i+1}\times t_{m-i-1} \]

注意到\(x-m+i+1+m-i-1=x\),那么这显然就是一个卷积式,我们可以\(O(n\log n)\)求出每一项,这样就可以\(O(n\log n)\)求出\(ans(x)\)了。

从这个算法得到启发,我们来总结做题的方法。

第一步:设计通配函数,即上文中的\(C(i,j)\)

第二步:设计完全通配函数,即上文中的\(ans(x)\)

第三步:计算每一位的完全通配函数的值。

来看几道简单的例题叭

1.P4173 残缺的字符串

题意:带通配符\(*\)的单模式串匹配。

思路:因为有通配符的存在,所以我们设计\(C(i,j)=s_it_j(s_i-t_j)^2\),为什么要这样设计呢?因为对应位置相等的情况要么就是\(s_i=t_j\ne *\),要么就是\(s_i=*||t_j=*\),因此我们把通配符当成0,这样设计匹配函数就可以满足上面的条件了。

显然,完全通配函数就是

\[ans(x)=\sum\limits_{i=0}^{m-1}s_{x-m+i+1}t_i(s_{x-m+i+1}-t_i)^2\\ =\sum\limits_{i=0}^{m-1}(s^3_{x-m+i+1}t_i-2s^2_{x-m+i+1}t^2_i+s_{x-m+i+1}t^3_i) \]

如何快速计算呢?我们把b翻转,就可以得到

\[ans(x)=\sum\limits_{i=0}^{m-1}(s^3_{x-m+i+1}t_{m-i-1}-2s^2_{x-m+i+1}t^2_{m-i-1}+s_{x-m+i+1}t^3_{m-i-1}) \]

这个形式就和卷积一样了,我们只需把\(s^3\)\(t\)卷起来,再把\(s^2\)\(t^2\)卷起来,最后把\(s\)\(t^3\)卷起来就可以了。复杂度\(O(n\log n)\)

2.CF528D Fuzzy Search

题意:定义两个字符串匹配当且仅当T中每一个字符可以在S中找到一个相距不超过k的相同字符,求T在S中出现次数,字符集大小为4。

思路:这一题乍一看不好直接设计通配函数,我们发现这一题的字符集大小只有4,于是尝试对每一位分开计算。具体的当处理到字符ch时,我们把和ch相同的当成1,其他的是0,然后对S串正反扫一遍把所有与1相距不超过k的位置也标成1,这时两位匹配当且仅当\(t_i=0\)\(t_i=s_i=1\),这样我们设计匹配函数\(C(i,j)=t_j(a_i-t_j)^2\)就满足条件了。

显然,完全匹配函数就是

\[ans(x)=\sum\limits_{i=0}^{m-1}t_{i}(s_{x-m+i+1}-t_i)^2\\ =\sum\limits_{i=0}^{m-1}s^2_{x-m+i+1}t_i-2s_{x-m+i+1}t^2_i+t^3_i \]

根据我们的定义,\(f_i,g_i\)为0或1,那\(f^k_i=f_i,g^k_i=g_i\),于是就把原式化简为

\[ans(x)=\sum\limits_{i=0}^{m-1}s_{x-m+i+1}t_i-2s_{x-m+i+1}t_i+t_i\\ =\sum\limits_{i=0}^{m-1}t_i-s_{x-m+i+1}t_i \]

同样的,把T翻转,那么

\[ans(x)=\sum\limits_{i=0}^{m-1}t_i-\sum\limits_{i=0}^{m-1}s_{x-m+i+1}t_{m-i-1} \]

这样就可以卷积了。复杂度\(O(n\log n)\)

3.P3763 [TJOI2017]DNA

题意:求有多少个S的子串满足修改小于等于3个位置能够变成T,字符集大小为4。

思路:看到字符集大小很小,就想到对每一种字符单独考虑。S、T中和字符c相等的位置为1,定义匹配函数为\(C(i,j)=s_i\times t_j\),那自然\(C(i,j)=1\)\(s_i\)\(t_j\)匹配。

这时完全匹配函数就呼之欲出了。

\[ans(x)=\sum\limits_{i=0}^{m-1}s_{x-m+i+1}t_i \]

如果\(ans(x)+3\leqslant m\)就合法

显然,计算时将T翻转 (是不是很套路?) ,得:

\[ans(x)=\sum\limits_{i=1}^{m-1}s_{x-m+i+1}t_{m-i-1} \]

这样就可以愉快地卷积了。复杂度\(O(n\log n)\)

posted @ 2023-03-11 22:03  Xttttr  阅读(44)  评论(0编辑  收藏  举报