[洛谷P4173] 残缺的字符串
前言
经典问题。
题目
讲解
匹配两个字符串 \(S,T\) ,\(S\) 为模板串,考虑将字符转换为数字,为了方便,这里只考虑小写字母,\(a\rightarrow 1\)。
然后我们定义距离函数 \(dis(f,g)=\sum |f_i-g_i|\),绝对值不方便,所以我们平方。
\(dis(f,g)=\sum (f_i-g_i)^2\),这玩意不能快速求,考虑翻转 \(f\),距离函数变为 \(dis(f,g)=\sum (f_{i-j}-g_j)^2\) ,显然可以用多项式乘法做,最后得到系数为 0 的地方就是成功匹配的末尾位置。
这道题有通配符 *
,考虑 \(*\rightarrow 0\),更新距离函数:\(dis(f,g)=\sum (f_{i-j}-g_j)^2f_{i-j}g_j\)
暴力展开后有三项,分别做多项式乘法即可。这道题用FFT就行了,NTT会被卡。
注意 eps 不能设小了。
代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 1 << 20 | 5;
const double PI = acos(-1);
const double eps = 0.5;
int lena,lenb,cnt,na[MAXN],nb[MAXN];
LL Read()
{
LL x = 0,f = 1; char c = getchar();
while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
struct cp
{
double x,y;
cp(){}
cp(double x1,double y1){
x = x1;
y = y1;
}
cp operator + (const cp &A)const{return cp(x+A.x,y+A.y);}
cp operator - (const cp &A)const{return cp(x-A.x,y-A.y);}
cp operator * (const cp &A)const{return cp(x*A.x-y*A.y,x*A.y+y*A.x);}
}a[MAXN],b[MAXN],c[MAXN],I;
int rev[MAXN],len;
void pre(int L)
{
int l = -1; len = 1;
while(len <= L) len <<= 1,l++;
for(int i = 0;i <= len;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l);
}
void FFT(cp *a,int f)
{
for(int i = 0;i < len;++ i) if(i < rev[i]) swap(a[i],a[rev[i]]);
for(int i = 1;i < len;i <<= 1)
for(int j = 0,p = i << 1;j < len;j += p)
{
cp mi = cp(1,0),w = cp(cos(PI/i),f*sin(PI/i));
for(int k = 0;k < i;++ k,mi = mi * w)
{
cp X = a[j+k],Y = a[i+j+k];
a[j+k] = X + mi * Y;
a[i+j+k] = X - mi * Y;
}
}
if(f == -1) for(int i = 0;i < len;++ i) a[i].x /= len;
}
char gc()
{
char c = getchar();
while((c > 'z' || c < 'a') && c != '*') c = getchar();
return c;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
lena = Read(); lenb = Read();
for(int i = lena-1;i >= 0;-- i) na[i] = Max(gc()-'a'+1,0);
for(int i = 0;i < lenb;++ i) nb[i] = Max(gc()-'a'+1,0);
pre(lena+lenb);
for(int i = 0;i < len;++ i) if(i < lena) a[i] = cp{na[i] * na[i] * na[i],0}; else a[i] = I;
for(int i = 0;i < len;++ i) if(i < lenb) b[i] = cp{nb[i],0}; else b[i] = I;
FFT(a,1); FFT(b,1);
for(int i = 0;i < len;++ i) c[i] = c[i] + a[i] * b[i];
for(int i = 0;i < len;++ i) if(i < lena) a[i] = cp{na[i] * na[i],0}; else a[i] = I;
for(int i = 0;i < len;++ i) if(i < lenb) b[i] = cp{nb[i] * nb[i],0}; else b[i] = I;
FFT(a,1); FFT(b,1);
for(int i = 0;i < len;++ i) c[i] = c[i] + a[i] * b[i] * cp{-2,0};
for(int i = 0;i < len;++ i) if(i < lena) a[i] = cp{na[i],0}; else a[i] = I;
for(int i = 0;i < len;++ i) if(i < lenb) b[i] = cp{nb[i] * nb[i] * nb[i],0}; else b[i] = I;
FFT(a,1); FFT(b,1);
for(int i = 0;i < len;++ i) c[i] = c[i] + a[i] * b[i];
FFT(c,-1);
for(int i = lena-1;i < lenb;++ i) if(Abs(c[i].x) < eps) ++cnt;
Put(cnt,'\n');
for(int i = lena-1;i < lenb;++ i) if(Abs(c[i].x) < eps) Put(i-lena+2,' ');
return 0;
}