Description
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。
你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这个位置开始连续m个字符形成的子串是否可能与A串完全匹配?
Input
第一行包含两个正整数m,n(1<=m<=n<=300000),分别表示A串和B串的长度。
第二行为一个长度为m的字符串A。
第三行为一个长度为n的字符串B。
两个串均仅由小写字母和*号组成,其中*号表示相应位置已经残缺。
Output
第一行包含一个整数k,表示B串中可以完全匹配A串的位置个数。
若k>0,则第二行输出k个正整数,从小到大依次输出每个可以匹配的开头位置(下标从1开始)。
Sample Input
3 7
a*b
aebr*ob
Sample Output
2
1 5
Solution
借了一个号,可以做权限题了。在讲题解之前,我必须要控诉,表达我一个下午挣扎的痛苦。这TM是一道正解为FFT的FFT卡常题!!
一个下午的TLE,先是写NTT,然后发现好像有点问题,但对拍没错,T。改成FFT,还是T(多半是废了)。
在和tututu的并肩奋战下,我灵机一现,加了register和输出优化等底层优化。终于卡过了QAQ。
这是一道FFT的套路题,对我而言又发现了一种新套路。长话短说就是把通配符设成0,然后字符串换成整数,构造函数F[i],我们神奇地发现,将短串反过来做一个卷积就是答案:
于是我们就把这三项卷一下,判断F[i]是否为0就行了,为0则代表以i为末尾可匹配。
神奇的FFT处理字符串。
最后将次数界改小了一点,跑了6000ms+。
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define maxn 300010
using namespace std;
int n, LS, LT, L;
char S[maxn], T[maxn];
long long F[maxn<<2];
int f[maxn], g[maxn], rev[maxn<<2];
const double PI = acos(-1.0);
struct Complex{
double real, image;
Complex() {}
Complex(double _real, double _image){
real = _real;
image = _image;
}
friend Complex operator + (Complex A, Complex B){
return Complex(A.real + B.real, A.image + B.image);
}
friend Complex operator - (Complex A, Complex B){
return Complex(A.real - B.real, A.image - B.image);
}
friend Complex operator * (Complex A, Complex B){
return Complex(A.real * B.real - A.image * B.image, A.real * B.image + A.image * B.real);
}
}a[maxn<<2], b[maxn<<2];
inline void Reverse(){
for(register int i = 0; i < n; i++)
rev[i] = (rev[i>>1]>>1) | ((i&1)<<(L-1));
}
inline void FFT(Complex *A, int DFT){
for(register int i = 0; i < n; i++) if(rev[i] > i) swap(A[i], A[rev[i]]);
for(register int s = 1; (1<<s) <= n; s++){
int m = 1 << s;
Complex wn = Complex(cos(DFT*2*PI/m), sin(DFT*2*PI/m));
for(register int k = 0; k < n; k += m){
Complex w = Complex(1, 0);
for(register int j = 0; j < (m>>1); j++){
Complex u = A[k+j], t = w * A[k+j+(m>>1)];
A[k+j] = u + t;
A[k+j+(m>>1)] = u - t;
w = w * wn;
}
}
}
if(DFT == -1) for(register int i = 0; i < n; i++) A[i].real /= n;
}
void Work(int op){
FFT(a, 1);
FFT(b, 1);
for(register int i = 0; i < n; i++) a[i] = a[i] * b[i];
FFT(a, -1);
for(register int i = 0; i < n; i++) F[i] += (long long)(a[i].real + .5) * op;
}
inline int GET(int x){
int t = 1;
while(t < x) t <<= 1, L ++;
return t;
}
int sum, ans[maxn];
void Print(int x){
if(x > 9) Print(x / 10);
putchar(x % 10 + '0');
}
int main(){
scanf("%d%d", &LS, <);
scanf("%s%s", S, T);
n = GET(LT+LS);
Reverse();
for(register int i = 0; i < LS; i++){
if(S[LS-i-1] == '*') f[i] = 0;
else f[i] = S[LS-i-1] - 'a' + 1;
}
for(register int i = 0; i < LT; i++){
if(T[i] == '*') g[i] = 0;
else g[i] = T[i] - 'a' + 1;
}
for(register int i = 0; i < n; i++) a[i] = b[i] = Complex(0, 0);
for(register int i = 0; i < LS; i++) a[i] = Complex(f[i]*f[i]*f[i], 0);
for(register int i = 0; i < LT; i++) b[i] = Complex(g[i], 0);
Work(1);
for(register int i = 0; i < n; i++) a[i] = b[i] = Complex(0, 0);
for(register int i = 0; i < LS; i++) a[i] = Complex(f[i]*f[i], 0);
for(register int i = 0; i < LT; i++) b[i] = Complex(g[i]*g[i], 0);
Work(-2);
for(register int i = 0; i < n; i++) a[i] = b[i] = Complex(0, 0);
for(register int i = 0; i < LS; i++) a[i] = Complex(f[i], 0);
for(register int i = 0; i < LT; i++) b[i] = Complex(g[i]*g[i]*g[i], 0);
Work(1);
for(register int i = LS-1; i < LT; i++)
if(!F[i]){
sum ++;
ans[sum] = i - LS + 2;
}
printf("%d\n", sum);
for(register int i = 1; i <= sum; i++)
printf("%d ", ans[i]);
puts("");
return 0;
}