← 全部文章

P4173 残缺的字符串 题解

P4173 残缺的字符串

带通配符的字符串匹配模板题。

两个长度为 nn 的字符串 a,ba, b 相等当且仅当 ∑i=1n(ai−bi)2=0\sum_{i=1}^n (a_i-b_i)^2 = 0。

如果带有通配符,将通配符看成 00,则 a,ba,b 相等当且仅当 ∑i=1n(ai−bi)2aibi=0\sum_{i=1}^n (a_i-b_i)^2a_ib_i = 0。

套路地将 aa 串翻转,则 a,ba,b 相等当且仅当: ∑i=1n(ai−bn+1−i)2aibn+1−i=0∑i=1n(ai2−2aibn+1−i+bn+1−i2)aibn+1−i=0∑i=1nai3bn+1−i−∑i=1n2ai2bn+1−i2+∑i=1naibn+1−i3=0 \begin{aligned} \sum_{i=1}^n (a_i-b_{n+1-i})^2a_ib_{n+1-i} &= 0 \\\\ \sum_{i=1}^n (a_i^2-2a_ib_{n+1-i}+b_{n+1-i}^2)a_ib_{n+1-i} &= 0 \\\\ \sum_{i=1}^n a_i^3b_{n+1-i} - \sum_{i=1}^n 2a_i^2b_{n+1-i}^2 + \sum_{i=1}^n a_ib_{n+1-i}^3 &= 0 \\\\ \end{aligned} 最后即为三个卷积,可以 O(nlog⁡n)\mathcal O(n \log n) 判断。

回到本题,首先翻转 aa 串,设 f(p)=∑i=1nai3bn+p−i−∑i=1n2ai2bn+p−i2+∑i=1naibn+p−i3f(p) = \sum_{i=1}^n a_i^3b_{n+p-i} - \sum_{i=1}^n 2a_i^2b_{n+p-i}^2 + \sum_{i=1}^n a_ib_{n+p-i}^3。

则 f(p)=0f(p) = 0 的 pp 即为合法的位置。

个人喜欢用 NTT,因为没有精度问题。当然会有冲突,但相当于 Hash,除了 CF 一般不会被卡。

namespace NTT {
	const int N = 1 << 21 | 1;
	int n, m, k, l, r[N];
	modint vk, g = 3, vg = 332748118, a[N], b[N];
	inline void ntt(modint *a, int n, int x) {
		for (int i = 0; i < n; i++) if (i < r[i]) swap(a[i], a[r[i]]);
		for (int o = 2, k = 1; o <= n; o <<= 1, k <<= 1) {
			modint W = (x > 0 ? g : vg) ^ ((P - 1) / o);
			for (int i = 0; i < n; i += o) {
				modint w = 1;
				for (int j = 0; j < k; j++, w *= W)
					a[i+j+k] *= w,
					a[i+j] += a[i+j+k],
					a[i+j+k] = a[i+j] - a[i+j+k] - a[i+j+k];
			}
		}
	}
	inline void solve() {
		k = 1, l = 0;
		while (k <= n + m) k <<= 1, ++l;
		vk = (modint)1 / k;
		for (int i = 0; i < k; i++)
			r[i] = (r[i>>1] >> 1) | ((i & 1) << (l - 1));
		for (int i = n + 1; i < k; i++) a[i] = 0;
		for (int i = m + 1; i < k; i++) b[i] = 0;
		ntt(a, k, 1), ntt(b, k, 1);
		for (int i = 0; i < k; i++) a[i] *= b[i];
		ntt(a, k, -1);
		for (int i = 0; i <= n + m; i++) a[i] *= vk;
	}
}

const int N = 3e5 + 7;
int n, m;
char a[N], b[N];
modint f[N];

int main() {
	rd(n, m), rds(a, n), rds(b, m);
	reverse(a + 1, a + n + 1);
	NTT::n = n, NTT::m = m;
	for (int i = 1; i <= n; i++)
		if (a[i] == '*') a[i] = 0;
	for (int i = 1; i <= m; i++)
		if (b[i] == '*') b[i] = 0;
	
	for (int i = 1; i <= n; i++)
		NTT::a[i] = a[i] * a[i] * a[i];
	for (int i = 1; i <= m; i++)
		NTT::b[i] = b[i];
	NTT::solve();
	for (int p = 1; p + n - 1 <= m; p++)
		f[p] += NTT::a[p+n];
	
	for (int i = 1; i <= n; i++)
		NTT::a[i] = a[i] * a[i];
	for (int i = 1; i <= m; i++)
		NTT::b[i] = b[i] * b[i];
	NTT::solve();
	for (int p = 1; p + n - 1 <= m; p++)
		f[p] -= 2 * NTT::a[p+n];
	
	for (int i = 1; i <= n; i++)
		NTT::a[i] = a[i];
	for (int i = 1; i <= m; i++)
		NTT::b[i] = b[i] * b[i] * b[i];
	NTT::solve();
	for (int p = 1; p + n - 1 <= m; p++)
		f[p] += NTT::a[p+n];
	
	vi ans;
	for (int p = 1; p + n - 1 <= m; p++)
		if (!f[p]) ans.pb(p);
	print(ans.size());
	for (int x : ans) print(x, ' ');
	return 0;
}

标签:NTT

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…