学习笔记:FFT与拉格朗日插值

多项式的表示形式#

系数表示与点值表示#

假设 f(x) 是一个 n 次多项式,则 f(x) 的系数表示为 f(x)=anxn+an1xn1++a0

f(x) 的点值表示为 (x0,f(x0)), (x1,f(x1)),,(xn,f(xn)),其中 ij, xixj

  • n+1 个点值可以表示一个 n 次多项式。

  • 在点值表示下 n 次多项式的乘法复杂的为 O(n)

    h(x)=f(x)g(x),则 i[0,n], h(xi)=f(xi)g(xi)

复数与单位根#

复数的指数形式#

a+bi=reiθ,其中 r=a2+b2, tanθ=ba

欧拉公式:eix=cosx+isinx

单位根#

xn=1 在复数域上的根称为 n 次单位根。n 次单位根有 n 个,形式为 ωnk=ei2kπn

单位根在复平面上等分单位圆。

单位根的性质:

  • ωnk=ω2n2k
  • ω2nk+n=ω2nk

快速傅里叶变换(Fast Fourier Transform)#

离散傅里叶变换(Discrete Fourier Transform)#

将多项式 A(x)=a0+a1x++an1xn1 转化为其点值形式 (ωnk, A(ωnk)), (k=0,1,,n1)

不妨令 n2 的整次幂。

把原式拆成奇偶两部分,即 A(x)=(a0+a2x2++an2xn2)+(a1+a3x3++an1xn1)

B(x)=a0+a2x++an2xn/21,  C(x)=a1+a3x++an2xn/21

A(x)=B(x2)+xC(x2)

对于 k[0,n/21]

A(ωnk)=B(ωn2k)+ωnkC(ωn2k)=B(ωn/2k)+ωnkC(ωn/2k)

对于另一半的点值,可用 A(ωnk+n/2) 表示,即

A(ωnk+n/2)=B(ωn2k+n)+ωnk+n/2C(ωn2k+n)=B(ωn/2k)ωnkC(ωn/2k)

具体的讲,当 n=8 时,各项存在如下关系:

T(n)=2T(n/2)+O(n)

可以直观看出,以这种方式将系数表示转化为点表示的时间复杂度为 O(nlogn)

对于具体实现

  • 分治(递归,常数大)。

  • 蝴蝶变换(bit-reversal permutation)(非递归,常数小)。

    观察上图第一行和最后一行各项的二进制表示,互相颠倒。

    因此可以预处理最后一行的系数,自下而上递推。

逆离散傅里叶变换(Inverse DFT)#

将多项式的点值表示 (ωnk, bk), (k=0,1,,n1) 转化为其系数表示 A(x)=a0+a1x++an1xn1

n×n 的矩阵 Ω,其中 Ωi,j=ωnij,设向量 a=(a0,a1,,an1), b=(b0,1,,bn1)

则 IDFT 相当于求解方程 Ωa=b

((ωn0)0(ωn0)1(ωn0)n1(ωn1)0(ωn1)1(ωn1)n1(ωnn1)0(ωnn1)1(ωnn1)n1)(a0a1an1)=(b0b1bn1)

如果我们能够求出 Ω 的逆,则 a=Ω1b

M=ΩΩ=((ωn0)0(ωn0)1(ωn0)n1(ωn1)0(ωn1)1(ωn1)n1(ωn(n1))0(ωn(n1))1(ωn(n1))n1)((ωn0)0(ωn0)1(ωn0)n1(ωn1)0(ωn1)1(ωn1)n1(ωnn1)0(ωnn1)1(ωnn1)n1)

其中

Mi,j=(ωni)0(ωn0)j+(ωni)1(ωn1)j+(ωni)n1(ωnn1)j=(ωnji)0+(ωnji)1+(ωnji)n1

相当于等比数列求和

Mi,j{1(wnji)n1wnji=0wnji1 即 ijni=j

Ω 满足 Ωi,j=ωij,有 ΩΩ=nI,因此

a=1nΩb

相当于给定 B(x)=b0+b1x++bn1xn1,求点值 ak=B(ωnk), (0k<n)

例题#

A * B Problem Plus#

题意:给定 a, b,求 a×ba, b<1050001(范围可以更大)。

a 看作 a0+a1101+a2102+,fft 后再处理进位。

submission

SP8372 TSUM#

题意:给定长度为 N 的数列 s,对于任意可能存在的 V,求满足 si+sj+sk=V,  i<j<k 的对数,|si|20000, N40000

构造多项式 A(x)=xsi

那么 ijk[si+sj+sk=V]=[xV]A3(x)

这样求得的数对不一定满足偏序关系,也不一定互不相同。

偏序关系很好处理,最后将答案除上 3! 即可。

现要除去存在位置相同的数对,考虑容斥。

性质 a1:i=j,性质 a2:i=k,性质 a3:j=k

  1. 减去满足三个性质中一个的方案。

    钦定两个位置相等。

    B(x)=x2si,产生的方案为 [xV](A(x)B(x))

  2. 加上满足三个性质中两个的方案。

    i=j=k

    C(x)=x3si,这部分的方案为 [xV]C(x)

  3. 减去满足三个性质中三个的方案。

    与第二部分相同。

则最终答案 D(x)=A3(x)3B(x)+2C(x)

可以先对 A(x), B(x), C(x) 分别做 dft,计算出 D(x) 的点值,最后再对 D(x) 做 idft。

submission

FFT 在字符串匹配中的应用#

普通的单模式串匹配#

问题概述:给定模式串 a(长度为 m)、文本串 b(长度为 n),求所有位置 x,满足 B 串以 x 结尾的连续 m 个字符与 a 串完全相同。

定义匹配函数 C(x,y)=[axby]2,当 C(x,y)=0 时则称 a 的第 x 个字符与 B 的第 y 个字符匹配。

定义完全匹配函数 P(x)=i=0m1[aib(xm+i+1)]2

当且仅当 P(x)=0 时以 x 结尾的连续 m 个字符与 $$ 匹a配。

s 串满足 si=a(mi1),即 a 串的翻转。

于是 P(x)=i=0m1[s(mi+1)b(xm+i+1)]2

暴力展开:P(x)=i=0m1s(mi+1)2+i=0m1b(xm+i+1)2i=0m12s(mi+1)b(xm+i+1)

第一项为常数。

第二项可以预处理 f(x)=i=0xbi2,计算 f(x)f(xm)

第三项等效于求 g(x)=i+j=xsibj

P(x)=T+f(x)f(xm)2g(x)

把字符串当作多项式,即 B(x)=b0+b1x+b2x2+

gSB 的卷积。

最后检验多项式 P 有多少系数为 0 的项。

带通配符的单模式串匹配#

问题概述:给定模式串 a(长度为 m),文本串 b(长度为 n),串的某些字符是 “通配符”(可以与任意字符匹配)。求所有位置 x,满足 b 串以 x 结尾的连续 m 个字符与 a 串完全相同。

总结思路:

  1. 定义匹配函数。
  2. 定义完全匹配函数。
  3. 快速计算每一位的完全匹配函数(翻转模式串化为卷积)。

设通配符的数值为 0

定义匹配函数 C(x,y)=[axby]2axby

则完全匹配函数 P(x)=i=0m1[aib(xm+i+1)]2aib(xm+i+1)

a 翻转,P(x)=i=0m1[s(mi+1)b(xm+i+1)]2sib(xm+i+1)

暴力展开:

P(x)=i=0m1[s(mi+1)b(xm+i+1)]2s(mi+1)b(xm+i+1)=i=0m1s(mi+1)3b(xm+i+1)+i=0m1s(mi+1)b(xm+i+1)3i=0m12s(mi+1)2b(xm+i+1)2=i+j=xsi3bj+i+j=xsibj32i+j=xsi2bj2

把右边写成多项式乘积的形式,则

P(x)=si3xibixi+sixibi3xi2si2xibi2xi

例题#

P4173 残缺的字符串#

模板题。

#include<bits/stdc++.h>
using namespace std;
using namespace numbers;

using ll = long long;
constexpr int N = 6e5 + 5, tot = 1 << 19;

struct Complex {
	double x, y;
	Complex operator + (const Complex &o) const {
		return {x + o.x, y + o.y};
	}
	Complex operator - (const Complex &o) const {
		return {x - o.x, y - o.y};
	}
	Complex operator * (const Complex &o) const {
		return {x * o.x - y * o.y, x * o.y + y * o.x};
	}
} a[N], b[N], c[N];

int rev[N];

void fft(Complex a[], int Inv) {
	for(int i = 0; i < tot; ++ i) {
		if(i < rev[i]) {
			swap(a[i], a[rev[i]]);
		}
	}
	for(int mid = 1; mid < tot; mid <<= 1) {
		auto w1 = Complex({cos(pi / mid), Inv * sin(pi / mid)});
		for(int i = 0; i < tot; i += mid * 2) {
			auto wk = Complex({1, 0});
			for(int j = 0; j < mid; ++ j, wk = wk * w1) {
				auto x = a[i + j], y = a[i + j + mid];
				a[i + j] = x + wk * y;
				a[i + j + mid] = x - wk * y;
			}
		}
	}
	if(Inv == -1) {
		for(int i = 0; i < tot; ++ i) {
			a[i].x /= tot;
		}
	}
}

string s, t;
int m, n, A[N], B[N];

int main() {
	cin.tie(0)->sync_with_stdio(0);
	
	cin >> m >> n >> s >> t;
	reverse(s.begin(), s.end());
	for(int i = 0; i < m; ++ i) {
		A[i] = s[i] == '*' ? 0 : s[i] - 'a' + 1;
	}
	for(int i = 0; i < n; ++ i) {
		B[i] = t[i] == '*' ? 0 : t[i] - 'a' + 1;
	}
	
	for(int i = 0; i < tot; ++ i) {
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << 18);
	}
	
	for(int i = 0; i < tot; ++ i) {
		a[i] = Complex({A[i] * A[i] * A[i], 0});
		b[i] = Complex({B[i], 0});
	}
	fft(a, 1), fft(b, 1);
	for(int i = 0; i < tot; ++ i) {
		c[i] = c[i] + a[i] * b[i];
	}
	
	for(int i = 0; i < tot; ++ i) {
		a[i] = Complex({A[i], 0});
		b[i] = Complex({B[i] * B[i] * B[i], 0});
	}
	fft(a, 1), fft(b, 1);
	for(int i = 0; i < tot; ++ i) {
		c[i] = c[i] + a[i] * b[i];
	}
	
	for(int i = 0; i < tot; ++ i) {
		a[i] = Complex({A[i] * A[i], 0});
		b[i] = Complex({B[i] * B[i], 0});
	}
	fft(a, 1), fft(b, 1);
	for(int i = 0; i < tot; ++ i) {
		c[i] = c[i] - Complex({2, 0}) * a[i] * b[i];
	}
	
	fft(c, -1);
	
	vector<int> ans;
	for(int i = m - 1; i < n; ++ i) {
		ll v = c[i].x + 0.5;
		if(v == 0) {
			ans.push_back(i - m + 2);
		}
	}
	
	cout << ans.size() << '\n';
	for(int x : ans) cout << x << ' ';
	return 0;
}
CF528D#

题意:给定只含 ATGC 的两个字符串 s, t 和一个非负整数 k,求有多少 i 满足任意 j[0,|t|),都存在 p[0,|s|) 使得 |i+jp|ksp=tj

s 的长度为 nt 的长度为 m

预处理第 i 个位置能否与字符 c 匹配,记做 bi,c

定义完全匹配函数 P(x),表示以 x 结尾的字符串与 t 匹配的个数,匹配成功当且仅当 P(x)=m

P(x)=ctti=cb(xm+i+1,c)

单独考虑每个字符,记 f(x,A)=i=0m1[ti=A][bxm+i+1,A]

按照套路将 t 翻转。

f(x,A)=i=0m1[tmi1=A][bxm+i+1,A]

T(x)=i0[ti=A]xi, B(x)=i0[bi,A]xi,则 f(i,A)=[xi]T(x)B(x)

submission

拉格朗日插值#

拉格朗日插值定理#

n 个点值 (xi,yi), (1in),满足 xixj, (ij),它们 唯一 确定一个 n1 次多项式 f(x) 满足

f(x)=i=1nyijixxjxixj

构造一个多项式 f(x)=f1(x)+f2(x)++fn(x)

满足 fi(xj)={yji=j0ij

待定系数,f1(x)=A(xx2)(xx3)(xxn)

带入 x1,解出 A=y1(x1x2)(x1x3)(x1xn),其余 fi 同理。

  • CRT 的关系?

    多项式模多项式:如果 f(x)=q(x)g(x)+r(x),其中 r(x) 的次数小于 g(x),则记做 f(x)r(x)(modg(x))

    多项式互质:两个多项式的公因式只有常数,则称这两个多项式互质。

    对于任意 i[1,n]

    f(x)f(xi)=a1(xxi)+a2(xxi2)+an1(xn1xin1)0(modxxi)

    有同余方程组

    {f(x)f(x1)(modxx1)f(x)f(x2)(modxx2)f(x)f(xn1)(modxxn1)

    显然有模数两两互质,满足中国剩余定理的使用条件。

  • 暴力实现:先算 g(x)=(xxi),再求 ng(x)/(xxi),复杂度 O(n2)

  • 特殊情况1:只要求一个 f(x0),复杂度 O(n2)

  • 特殊情况2:只要求一个 f(x0),满足 xi=i,复杂度 O(n)(可以预处理)。

例题#

CF622F#

题意:求 i=1nik(mod109+7), n109, k106

f(n)=i=1nik 是关于 nk+1 次多项式。

证明:

对于第二类斯特林数,满足公式

nm=i=0m{mi}ni

那么

f(n)=i=1nj=0k{kj}ij=j=0k{kj}j!i=1n(ij)

有组合恒等式

l=0n(lk)=(n+1k+1)

f(n)=i=0k{ki}i!(n+1i+1)=i=0k{ki}(n+1)i+1i+1

所以求出 [0,k+1] 的函数值,直接拉插即可。

submission

Polynomia#

题意:f(n)n 次多项式,给定 f(i), i[0,n],询问 s(R)s(L1)

f(n)n 次多项式,则其前缀和为 n+1 次多项式。

先对 f(n) 做一遍拉插求出 f(n+1),因而得到 s(n+1),再对 s 做拉插。

submission

reference#

Ebola:浅谈FFT在字符串匹配中的应用

posted @   Lu_xZ  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示