\(\text{Problem}\)

大概就是带通配符的字符串匹配问题,输出所有比配位置

\(1\le n \le 3\times 10^5\)

\(\text{Solution}\)

这是 \(FFT\) 在字符串匹配中的应用

默认下标以 \(0\) 开始,记通配符数值为 \(0\)

\(A\) 为文本串,考虑 \(A\) 从某一位 \(i\) 开始与 \(B\) 的匹配结果

构造一个函数描述这个结果 \(F_i = \sum_{j=0}^{m-1} (A_{i+j}-B_j)^2 A_{i+j} B_j\)

\(F_i = 0\) 则 \(i\) 这个开始匹配位是成功的

我们要计算每个 \(F_i\),这还是不高效

但考虑把 \(B\) 翻转,\(F_i = \sum_{j=0}^{m-1} (A_{i+j}-B_{m-j-1})^2 A_{i+j} B_{m-j-1}\)

发现 \(A,B\) 小标加起来恒为 \(i+m-1\) !

这是多项式卷积的形式!

那我们就考虑把 \(A,B\) 当做一个多项式,\(F(x)=A(x)B(x)\)

这里的 \(F(x)\) 第 \(i+m-1\) 项的系数就是原来 \(F_i\) 的结果

这样就可以 \(O(n \log n)\) 做匹配了

\(\text{Code}\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#define IN inline
#define RE register
using namespace std;
typedef long long LL; const int N = 1e6 + 1e5, P = 998244353, g = 3;
int n, m, A[N], B[N], rev[N];
LL a[N], b[N], F[N];
char s[N]; IN int fpow(LL x, int y){LL s = 1; for(; y; y >>= 1, x = x * x % P) if (y & 1) s = s * x % P; return s;}
IN void NTT(LL *a, int lim, int inv)
{
if (lim == 1) return;
for(RE int i = 0; i < lim; i++) if (i < rev[i]) swap(a[i], a[rev[i]]);
for(RE int mid = 1; mid < lim; mid <<= 1)
{
int I = fpow(g, (P - 1) / (mid << 1));
if (inv == -1) I = fpow(I, P - 2);
for(RE int i = 0; i < lim; i += (mid << 1))
{
LL W = 1;
for(RE int j = 0, x, y; j < mid; j++, W = W * I % P)
x = a[i + j], y = W * a[i + j + mid] % P,
a[i + j] = (x + y) % P, a[i + j + mid] = (x - y + P) % P;
}
}
} int main()
{
scanf("%d%d%s", &m, &n, s);
for(RE int i = 0; i < m; i++) if (s[i] == '*') A[i] = 0; else A[i] = s[i] - 'a' + 1;
scanf("%s", s);
for(RE int i = 0; i < n; i++) if (s[i] == '*') B[i] = 0; else B[i] = s[i] - 'a' + 1;
int lim = 1; while (lim < n + m - 1) lim <<= 1;
int bit = 0; while ((1 << bit) < lim) ++bit;
for(RE int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(bit-1));
reverse(A, A + m); for(RE int i = 0; i < lim; i++) a[i] = A[i] * A[i] * A[i], b[i] = B[i];
NTT(a, lim, 1), NTT(b, lim, 1);
for(RE int i = 0; i < lim; i++) F[i] = a[i] * b[i] % P; for(RE int i = 0; i < lim; i++) b[i] = B[i] * B[i] * B[i], a[i] = A[i];
NTT(a, lim, 1), NTT(b, lim, 1);
for(RE int i = 0; i < lim; i++) F[i] = (F[i] + a[i] * b[i] % P) % P; for(RE int i = 0; i < lim; i++) a[i] = A[i] * A[i], b[i] = B[i] * B[i] % P;
NTT(a, lim, 1), NTT(b, lim, 1);
for(RE int i = 0; i < lim; i++) F[i] = (F[i] - a[i] * b[i] * 2 % P + P) % P;
NTT(F, lim, -1); int inv = fpow(lim, P - 2);
for(RE int i = 0; i < lim; i++) F[i] = F[i] * inv % P; int ans = 0;
for(RE int i = m - 1; i < n; i++) if (!F[i]) ++ans;
printf("%d\n", ans);
for(RE int i = m - 1; i < n; i++) if (!F[i]) printf("%d ", i - m + 2);
}

LG P4173 残缺的字符串的更多相关文章

  1. 洛谷 P4173 残缺的字符串 (FFT)

    题目链接:P4173 残缺的字符串 题意 给定长度为 \(m\) 的模式串和长度为 \(n\) 的目标串,两个串都带有通配符,求所有匹配的位置. 思路 FFT 带有通配符的字符串匹配问题. 设模式串为 ...

  2. Luogu P4173 残缺的字符串-FFT在字符串匹配中的应用

    P4173 残缺的字符串 FFT在字符串匹配中的应用. 能解决大概这种问题: 给定长度为\(m\)的A串,长度为\(n\)的B串.问A串在B串中的匹配数 我们设一个函数(下标从\(0\)开始) \(C ...

  3. P4173 残缺的字符串(FFT字符串匹配)

    P4173 残缺的字符串(FFT字符串匹配) P4173 解题思路: 经典套路将模式串翻转,将*设为0,设以目标串的x位置匹配结束的匹配函数为\(P(x)=\sum^{m-1}_{i=0}[A(m-1 ...

  4. P4173 残缺的字符串 fft

    题意:给你两个字符串,问你第一个在第二个中出现过多少次,并输出位置,匹配时是模糊匹配*可和任意一个字符匹配 题解:fft加速字符串匹配; 假设上面的串是s,s长度为m,下面的串是p,p长度为n,先考虑 ...

  5. 洛谷P4173 残缺的字符串(FFT)

    传送门 话说为什么字符串会和卷积扯上关系呢……到底得脑洞大到什么程度才能想到这种东西啊……大佬太珂怕了…… 因为通配符的关系,自动机已经废了 那么换种方式考虑,如果两个字符串每一位对应的编码都相等,那 ...

  6. 洛谷P4173 残缺的字符串

    题目大意: 两个带通配符的字符串\(a,b\),求\(a\)在\(b\)中出现的位置 字符串长度\(\le 300000\) 考虑魔改一发\(kmp\),发现魔改不出来 于是考虑上网搜题解 然后考虑\ ...

  7. [Luogu P4173]残缺的字符串 ( 数论 FFT)

    题面 传送门:洛咕 Solution 这题我写得脑壳疼,我好菜啊 好吧,我们来说正题. 这题.....emmmmmmm 显然KMP类的字符串神仙算法在这里没法用了. 那咋搞啊(或者说这题和数学有半毛钱 ...

  8. luogu P4173 残缺的字符串

    传送门 两种做法,一种是依次考虑每种字符,然后如果某个位置是该字符或者是\(*\)对应的值就是1,否则是0,然后把第一个串倒过来,fft卷积起来,最后看对应位置的值是否为m 然而上面那个做法在字符集大 ...

  9. P4173 残缺的字符串

    题目链接 题意分析 啥 ? ? ? \(FFT\)做字符串匹配 可是就是这样 我们定义匹配函数 我们定义\(A\)是匹配串 \(B\)是被匹配串 我们当前到达\(B\)串的\(x\)位置 \[P(x) ...

  10. 洛谷 P4173 残缺的字符串

    (不知道xjb KMP可不可以做的说) (假设下标都以0开头) 对于有一定偏移量的序列的 对应位置 匹配或者数值计算的题,这里是有一种套路的,就是把其中一个序列翻转过来,然后卷积一下,所得到的新序列C ...

随机推荐

  1. Composer 部署国内镜像

    众所周知的原因,原版的镜像下载会比较慢,建议改成阿里的会比较快. 1 备份你的原镜像文件,以免出错后可以恢复.mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum ...

  2. conky配置(附配置项作用解释)

    alignment top_right #是否嵌入桌面 background yes #是否绘制窗口边框 draw_borders no #窗口边框 border_width 10 #cpu_avg_ ...

  3. 这么简单,还不会使用java8 stream流的map()方法吗?

    一.前言 在日常的开发工作中经常碰到要处理list中数据的问题,比如从数据库中查出了很多学生,由于一些原因需要在内存中找出这些学生中的所有姓名,或者把名为"王五"的语文成绩暂时修改 ...

  4. 【Shell案例】【awk map计数&sort按指定列排序】9、统计每个单词出现的个数

    描述写一个 bash脚本以统计一个文本文件 nowcoder.txt 中每个单词出现的个数. 为了简单起见,你可以假设:nowcoder.txt只包括小写字母和空格.每个单词只由小写字母组成.单词间由 ...

  5. static_cast和dynamic_cast

    C++的强制类型转换,除了继承自C语言的写法((目标类型)表达式)之外,还新增了4个关键字,分别是:static_cast.dynamic_cast.const_cast和reinterpret_ca ...

  6. 【nginx】代理设置Host

    旧文章从语雀迁移过来,原日期为2021-02-18 nginx 的 proxy 模块使我们经常会用到的模块之一,比如我们常用的 nginx 反向代理. 反向代理我们一般有这么几行配置代码: locat ...

  7. 如何使用 Blackbox Exporter 监控 URL?

    前言 监控域名和 URL 是可观察性的一个重要方面,主要用于诊断可用性问题.接下来会详细介绍如何使用 Blackbox Exporter 和 Prometheus 在 Kubernetes 中实现 U ...

  8. 我的基于 JamStack 的新博客

    概述 今天心血来潮,介绍一下我的新博客站点 -- https://EWhisper.cn. 我是做基础平台 PaaS 运维和架构的,挺喜欢把工作中学到的新知识写下来.记笔记,突然有一天就抱着「资源共享 ...

  9. Asp-Net-Webapi项目从Framework-4-5升级到-Net-6的总结

    title: Asp.Net Webapi项目从Framework 4.5升级到.Net 6的总结 date: 2022-10-06 14:31:36 tags: - .NET 前言 目前手头上有个项 ...

  10. [cocos2d-x]判断两个矩形是否有交叉区域

    bool Rect::intersectsRect(const Rect& rect) const { return !( getMaxX() < rect.getMinX() || r ...