zoukankan      html  css  js  c++  java
  • P4173 残缺的字符串

    (color{#0066ff}{ 题目描述 })

    很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串(A)(B),其中(A)串长度为(m)(B)串长度为(n)。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。

    你想对这两个串重新进行匹配,其中(A)为模板串,那么现在问题来了,请回答,对于(B)的每一个位置(i),从这个位置开始连续(m)个字符形成的子串是否可能与(A)串完全匹配?

    (color{#0066ff}{输入格式})

    第一行包含两个正整数(m),(n),分别表示(A)串和(B)串的长度。

    第二行为一个长度为(m)的字符串(A)

    第三行为一个长度为(n)的字符串(B)

    两个串均仅由小写字母和*号组成,其中*号表示相应位置已经残缺。

    (color{#0066ff}{输出格式})

    第一行包含一个整数(k),表示BB串中可以完全匹配(A)串的位置个数。

    (k > 0),则第二行输出(k)个正整数,从小到大依次输出每个可以匹配的开头位置(下标从(1)开始)。

    (color{#0066ff}{输入样例})

    3 7
    a*b
    aebr*ob
    

    (color{#0066ff}{输出样例})

    2
    1 5
    

    (color{#0066ff}{数据范围与提示})

    100%的数据满足(1 leq m leq n leq 300000)

    (color{#0066ff}{ 题解 })

    构造两个序列

    如果字符为*,则序列对应位置为0, 否则为字符ASCII码

    这样如果某位置匹配,当且仅当(sum (a_i-b_j)^2*a_i*b_j=0)

    为什么要平方? 因为有可能上下一个是ab,一个是ba,这样一个正一个负,加完后还是0,但是不匹配

    为什么不用绝对值? 不好处理

    这样这个式子可以展开

    (sum a_i^3b_{j-i}+sum a_ib_{j-i}^3-sum a_i^2b_{j-i}^2)

    分别fft一下就行了

    注意最后输出的时候,循环的范围,要合法!

    这题还卡常,不开O2下面的代码TLE。。。

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    using std::vector;
    const int maxn = 2e6 + 10;
    const int mod = 998244353;
    int r[maxn], len;
    LL ksm(LL x, LL y) {
    	LL re = 1LL;
    	while(y) {
    		if(y & 1) re = re * x % mod;
    		x = x * x % mod;
    		y >>= 1;
    	}
    	return re;
    }
    void FNTT(vector<int> &A, int len, int flag) {
    	A.resize(len);
    	for(int i = 0; i < len; i++) if(i < r[i]) std::swap(A[i], A[r[i]]);
    	for(int l = 1; l < len; l <<= 1) {
    		int w0 = ksm(3, (mod - 1) / (l << 1));
    		for(int i = 0; i < len; i += (l << 1)) {
    			int w = 1, a0 = i, a1 = i + l;
    			for(int k = 0; k < l; k++, a0++, a1++, w = 1LL * w * w0 % mod) {
    				int tmp = 1LL * A[a1] * w % mod;
    				A[a1] = ((A[a0] - tmp) % mod + mod) % mod;
    				A[a0] = (A[a0] + tmp) % mod;
    			}
    		}
    	}
    	if(!(~flag)) {
    		std::reverse(A.begin() + 1, A.end());
    		int inv = ksm(len, mod - 2);
    		for(int i = 0; i < len; i++) A[i] = 1LL * A[i] * inv % mod;
    	}
    }
    vector<int> operator * (vector<int> A, vector<int> B) {
    	int tot = A.size() + B.size() - 1;
    	FNTT(A, len, 1);
    	FNTT(B, len, 1);
    	vector<int> ans;
    	ans.resize(len);
    	for(int i = 0; i < len; i++) ans[i] = 1LL * A[i] * B[i] % mod;
    	FNTT(ans, len, -1);
    	ans.resize(tot);
    	return ans;
    }
    char s[maxn], t[maxn];
    int ss[maxn], tt[maxn];
    signed main() {
    	int m = in(), n = in();
    	scanf("%s", s);
    	scanf("%s", t);
    	for(int i = 0; i < m; i++) ss[i] = s[i] == '*'? 0 : (int)s[i];
    	for(int i = 0; i < n; i++) tt[i] = t[i] == '*'? 0 : (int)t[i];
    	vector<int> A1, A2, A3, B1, B2, B3, ans, ls;
    	for(int i = 0; i < m; i++) {
    		A1.push_back(ss[i]);
    		A2.push_back(ss[i] * ss[i]);
    		A3.push_back(ss[i] * ss[i] * ss[i]);
    	}
    	for(int i = 0; i < n; i++) {
    		B1.push_back(tt[i]);
    		B2.push_back(tt[i] * tt[i]);
    		B3.push_back(tt[i] * tt[i] * tt[i]);
    	}
    	std::reverse(A1.begin(), A1.end());
    	std::reverse(A2.begin(), A2.end());
    	std::reverse(A3.begin(), A3.end());
    	int tot = A1.size() + B1.size() - 1;
    	for(len = 1; len <= tot; len <<= 1);
    	for(int i = 1; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
    	ls = A3 * B1;
    	for(int i = 0; i < n + m - 1; i++) ans.push_back(ls[i]);
    	ls = A1 * B3;
    	for(int i = 0; i < n + m - 1; i++) ans[i] = (ans[i] + ls[i]) % mod;
    	ls = A2 * B2;
    	for(int i = 0; i < n + m - 1; i++) ans[i] = ((ans[i] - 2LL * ls[i]) % mod + mod) % mod;
    	tot = 0;
    	for(int i = m - 1; i < n; i++) if(!ans[i]) tot++;
    	printf("%d
    ", tot);
    	for(int i = m - 1; i < n; i++) if(!ans[i]) printf("%d ", i - m + 2);
    	return 0;
    }
    
  • 相关阅读:
    重建二叉树
    字符串移位包含的问题
    整数的逆序存储
    容器的综合应用:文本查询程序(摘自C++ Primer)
    vsprintf 变参函数可以用
    常用项目依赖(前端)
    eslint一些常见配置
    Jscrpit中的原型对象
    html网页自适应手机屏幕大小
    A Bit of Fun
  • 原文地址:https://www.cnblogs.com/olinr/p/10270937.html
Copyright © 2011-2022 走看看