设匹配函数 C(x,y) 为字符 x 和字符 y 匹配的值,是我们自己定义的值。
两个字符串匹配的值就是对应位置上的字符匹配的值的和。
对于文本串 S 和模式串 T,现在要求出 T 在 S 中所有匹配的位置。
为了化成卷积的形式,把 T 反转。
这样 T 和 S 以 i 为结尾的子串的匹配值为:
|T|−1∑j=0C(Si−j,Tj)
为了能够区分不同位置的匹配值,定义生成函数:P(x),[xi] 代表 T 与 S 以 i 结尾的子串的匹配值。
那么:
P(x)=|S|∑i=|T|−1xi|T|−1∑j=0C(Si−j,Tj)
为了能通过观察匹配值来确定两个字符串,构造匹配函数 C(x,y)=(x−y)2(字符的权值需满足两两不同,可以使用 ASCII 码来作为权值),则两个字符串匹配当且仅当匹配值为 0.
那么:
P(x)=|S|∑i=|T|−1xi|T|−1∑j=0(Si−j−Tj)2=|S|∑i=|T|−1xi|T|−1∑j=0Si−j2+Tj2−2Si−jTj=|S|∑i=|T|−1xi|T|−1∑j=0Si−j2+|S|∑i=|T|−1xi|T|−1∑j=0Tj2−2|S|∑i=|T|−1|T|−1∑j=0Si−jxi−jTjxj
另外,为了使得边界满足卷积的形式,对于"溢出"的部分定义其权值为 0,也就是我们需要计算:
|S|∑i=|T|−1xi|T|−1∑j=0Si−j2+|S|∑i=|T|−1xi|T|−1∑j=0Tj2−2|S|∑i=|T|−1i∑j=0Si−jxi−jTjxj
前两个柿子可以预处理前缀和来做到线性求出,后面是个卷积的形式,可以通过 FFT O(nlogn) 求出。
所以就可以 O(nlogn) 做到字符串匹配了。
栗题一 luoguP4173
带通配符的字符串匹配。
尝试构造匹配函数 C(x,y) 满足:
设通配符的权值为 0,C(x,y)=(x−y)2xy.
P(x)=|S|∑i=|T|−1xi|T|−1∑j=0(Si−j−Tj)2Si−jTj=S3∗T−2×S2∗T2+S∗T3
(这里的次方是点乘,∗ 为卷积)
三次卷积,时间复杂度 O(nlogn).
栗题二 Codeforces 528 D
分别仅考虑 A,C,G,T,把匹配成功的位置取个交集就可以。
现在仅考虑 A,把 S 中不会和 A 匹配上的位置上的字符设为 o,把 T 中不是 A 的字符设为 #,则匹配函数 C(x,y) (其中 x 来自 S,y 来自 T)要满足:
- =0,x=A,y=A;
- =0,x=A,y=#;
- >0,x=o,y=A;
- =0,x=o,y=#.
这样的 C 才能满足匹配成功值为 0,否则大于 0.
设:
-
S 中的 A 值为 0,o 值为 1;
-
T 中的 A 值为 1,# 值为 0;
-
C(x,y)=xy.
对于每个字符只需要一次 FFT 就可以了。
Code
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?