P4173 残缺的字符串 题解
带通配符的字符串匹配模板题。
两个长度为 的字符串 相等当且仅当 。
如果带有通配符,将通配符看成 ,则 相等当且仅当 。
套路地将 串翻转,则 相等当且仅当: 最后即为三个卷积,可以 判断。
回到本题,首先翻转 串,设 。
则 的 即为合法的位置。
个人喜欢用 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;
}
评论
暂无已公开评论。