LG P4173 残缺的字符串

\(\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);
}
原文地址:https://www.cnblogs.com/leiyuanze/p/15820785.html