4503: 两个串
4503: 两个串
https://www.lydsy.com/JudgeOnline/problem.php?id=4503
题意:
求第二个串在第一个中出现了几次,用通配符。求出每个串的起始位置。
分析:
bitset。
一共有26个字母,求出每个字母在第一个串中出现的位置。扫一遍第二个串,ans = p[[1]] & (p[T[2]]>>1) & (p[T[3]>>3]) & ... & p[T[n]>>n]。很好理解,就是每个字符所有出现的位置往右移,如果匹配,那么这一位and后,为1。
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<cctype> #include<bitset> using namespace std; const int N = 200001; bitset<N> p[26], ans; char s[N], t[N]; int main() { scanf("%s", s); int l1 = strlen(s); scanf("%s", t); int l2 = strlen(t); for (int j=0; j<26; ++j) for (int i=0; i<l1; ++i) if (s[i] - 'a' == j) p[j].set(i); for (int i=0; i<=l1-l2; ++i) ans.set(i); for (int i=0; i<l2; ++i) { if (t[i] == '?') continue; ans &= (p[t[i] - 'a'] >> i); } cout << ans.count() << "\n"; for (int i=0; i<l1; ++i) if (ans[i]) printf("%d\n", i); return 0; }
FFT的做法:
如果T出现在S中,那么有$\sum\limits_{i=0}^{m-1} (S_i-T_i)^2=0$
如果出现通配符,如果T出现在S中,那么设T[i]=0,那么有$\sum\limits_{i=0}^{m-1} (S_i-T_i)^2 \times T[i]=0$
然后将式子拆开$\sum\limits_{i=0}^{m-1} S_i^2T_i-2S_iT_i^2+T_i^3=0$
把T翻转,然后FFT即可。
当然,这只是对应到第一个位置的情况,其他的情况类似只需要将卷积后的指数改一下即可。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 270005; const double Pi = acos(-1.0), eps = 1e-10; struct Com{ double x, y; Com(double _x = 0, double _y = 0) { x = _x, y = _y; } }a[N], b[N], a2[N], b2[N], C[N]; double b3; Com operator + (const Com &A,const Com &B) { return Com(A.x + B.x, A.y + B.y); } Com operator - (const Com &A,const Com &B) { return Com(A.x - B.x, A.y - B.y); } Com operator * (const Com &A,const Com &B) { return Com(A.x * B.x - A.y * B.y, A.x * B.y + A.y * B.x); } char s[N], t[N]; int rev[N], S[N], T[N], c[N]; vector<int> ans; void FFT(Com *a,int len,int ty) { for (int i = 0; i < len; ++i) if (i < rev[i]) swap(a[i], a[rev[i]]); Com w1, w, u, t; for (int m = 2; m <= len; m <<= 1) { w1 = Com(cos(2 * Pi / m), ty * sin(2 * Pi / m)); for (int i = 0; i < len; i += m) { w = Com(1, 0); for (int k = 0; k < (m >> 1); ++k) { u = a[i + k], t = w * a[i + k + (m >> 1)]; a[i + k] = u + t; a[i + k + (m >> 1)] = u - t; w = w * w1; } } } } int main() { scanf("%s%s", s, t); int n = strlen(s), m = strlen(t), len = 1, lg = 0; while (len < n + m) len <<= 1, lg ++; for (int i = 0; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1)); for (int i = 0; i < n; ++i) S[i] = s[i] - 'a' + 1; for (int i = 0; i < m; ++i) T[i] = t[i] == '?' ? 0 : t[i] - 'a' + 1; for (int i = 0; i < n; ++i) a[i] = Com(2 * S[i], 0), a2[i] = Com(S[i] * S[i], 0); reverse(T, T + m); for (int i = 0; i < m; ++i) b[i] = Com(T[i], 0), b2[i] = Com(T[i] * T[i], 0), b3 += T[i] * T[i] * T[i]; FFT(a2, len, 1); FFT(b, len, 1); FFT(a, len, 1); FFT(b2, len, 1); for (int i = 0; i < len; ++i) C[i] = a2[i] * b[i] - a[i] * b2[i]; FFT(C, len, -1); for (int i = 0; i < len; ++i) C[i].x = C[i].x / (double)len; for (int i = 0; i < len; ++i) c[i] = floor(C[i].x + b3 + 0.5); for (int i = m - 1; i < n; ++i) if (c[i] == 0) ans.push_back(i - m + 1); printf("%d\n", (int)ans.size()); for (int i = 0; i < (int)ans.size(); ++i) printf("%d\n", ans[i]); return 0; }