[洛谷P4173] 残缺的字符串

前言

经典问题。

题目

洛谷

DarkBZOJ

讲解

匹配两个字符串 \(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;
}
posted @ 2022-01-06 17:58  皮皮刘  阅读(41)  评论(0编辑  收藏  举报