• 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);
    }
    
  • 相关阅读:
    Python 文件的输入与输出
    php获取客户端真实ip
    php设计模式(3)-观察者模式
    php设计模式(2)-单例模式
    php设计模式(1)-工厂模式
    设计模式
    设计模式-观察者模式
    include和require的区别
    php分页类
    反向Ajax,第5部分:事件驱动的Web开发
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/15820785.html
Copyright © 2020-2023  润新知