形如:
f ( x ) = ∑ 0 n − 1 a i ⋅ x i f(x)=\sum_{0}^{n-1}ai\cdot x^i f(x)=0∑n−1ai⋅xi
就是上式的写法
在f(x)上取n个点,就能唯一确定的表示出这个多项式。
证明如下:
∀ \forall ∀n点集合c
定义集合A={ a 0 , a 1 , a 2 , . . . , a n − 1 a_0,a_1,a_2,...,a_{n-1} a0,a1,a2,...,an−1}为某一个的n点的集合c所表示的多项式的系数集合。
反证:
若不然,则 ∃ \exists ∃集合B,B ̸ = \not = ̸=A,并且B也是c所表示多项式的系数集合。
此时,令Ci=Ai-Bi,那么带入c中的每一个元素,C所代表的多项式的值都会是0,也就是说,该n-1次多项式有n个零点,这显然与事实不符,所以证毕。
如果用系数表示法直接相乘,那么计算的复杂度是O( n 2 n^{2} n2)的。
但是如果用点值表示法相乘,由于函数的相乘是每一个点的两个函数值相乘,所以复杂度是O(n)的。
在数学上,我们定义i,满足 i 2 = − 1 i^2=-1 i2=−1也就是说 i = − 1 i=\sqrt{-1} i=−1
定义一个复数为 a + b i a+bi a+bi那么由此我们就把数的定义扩充到了更大的范围。
复数的定义 a + b i a+bi a+bi,我们可以把a看成x轴,b看成y轴,那么每一个复数都可以看做在复平面上的一个点。当然,原本的实数就是复平面中x轴上的点。
复数的运算法则: ( a 1 + b 1 i ) ⋅ ( a 2 + b 2 i ) = (a_1+b_1i)\cdot(a_2+b_2i)= (a1+b1i)⋅(a2+b2i)=
a 1 b 1 + a 1 b 2 i + a 2 b 1 i + b 1 b 2 i 2 = a_1b_1+a_1b_2i+a_2b_1i+b_1b_2i^2= a1b1+a1b2i+a2b1i+b1b2i2=
a 1 b 1 − b 1 b 2 + ( a 1 b 2 + a 2 b 1 ) i a_1b_1-b_1b_2+(a_1b_2+a_2b_1)i a1b1−b1b2+(a1b2+a2b1)i
复平面上的复数可以看做向量,那么复平面上的复数可以表示为(辐角(与x轴的夹角大小),长度)。对于这种表示方法,向量的相加的法则是:长度相乘,辐角相加。
证明:
假设两个复数 a 1 + b 1 i , a 2 + b 2 i a_1+b_1i,a_2+b_2i a1+b1i,a2+b2i
那么两个复数的长度分别为 a 1 2 + b 1 2 , a 2 2 + b 2 2 \sqrt{{a_1}^2+{b_1}^{2}},\sqrt{{a_2}^2+{b_2}^{2}} a12+b12,a22+b22
相乘等于
a 1 2 a 2 2 + a 1 2 b 2 2 + b 1 2 a 2 2 + b 1 2 b 2 2 \sqrt{{a_1}^2{a_2}^2+{a_1}^2{b_2}^2+{b_1}^2{a_2}^2+{b_1}^2{b_2}^2} a12a22+a12b22+b12a22+b12b22
而两个向量相乘后得到的向量 a 1 b 1 − b 1 b 2 + ( a 1 b 2 + a 2 b 1 ) i a_1b_1-b_1b_2+(a_1b_2+a_2b_1)i a1b1−b1b2+(a1b2+a2b1)i的长度也等于上式的值。
辐角相加也类似可证,只不过需要用到定理 tan a + b = tan a + tan b 1 − tan a tan b \tan a+b=\frac{\tan a+\tan b}{1-\tan a\tan b} tana+b=1−tanatanbtana+tanb,这里就省略了。
定义 ω n \omega_n ωn为在复平面上,以1为半径作圆,把圆等分成n份。
那么 ω n k \omega_n^k ωnk就表示这个n等分圆中,从x轴开始,逆时针数的第k条等分线所表示的复数。
我们规定x轴上的等分线为第0条,同时也是的n条,也就是说 ω n 0 = ω n n = 1 \omega_n^0=\omega_n^n=1 ωn0=ωnn=1,意思就是等分从x轴开始。
那么有一个显然的计算 ω n k \omega_n^k ωnk的方法: ω n k = cos k 2 π n + sin k 2 π n ⋅ i \omega_n^k=\cos k\frac{2\pi}{n}+\sin k\frac{2\pi}{n}\cdot i ωnk=coskn2π+sinkn2π⋅i
ω 2 n 2 k = ω n k \omega_{2n}^{2k}=\omega_n^k ω2n2k=ωnk
这个可以感性理解一下,一个圆分成n份,取第k份,和一个圆分成2n份,取第2k份,实际上是相同的。
ω n k + n 2 = − ω n k \omega_{n}^{k+\frac{n}{2}}=-\omega_n^k ωnk+2n=−ωnk
这个性质也很好理解,画一个图即可。
FFT是实现傅里叶变换(DFT)和离散傅里叶变换(IDFT)的方法
DFT实际上是将多项式由系数表示法变为点值表示法的过程。
假设有一个n次的多项式
如果用暴力的方法实现这一过程,那么就是随意取n个点,分别带入多项式,求出此时的值,这样做的复杂度是O( n 2 n^2 n2)的。
那么我们考虑特殊的取点, ω n 0 , . . . , ω n n − 1 \omega_n^0,...,\omega_n^{n-1} ωn0,...,ωnn−1,带入这些点,会对我们的转变效率有什么提高呢?
我们考虑一个多项式F
F ( x ) = a 0 + a 1 x + a 2 x 2 + . . . + a n − 1 x n − 1 F(x)=a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1} F(x)=a0+a1x+a2x2+...+an−1xn−1
我们把它分成偶次 F 1 F_1 F1和奇次 F 2 F_2 F2两个部分
F 1 ( x ) = a 0 + a 2 x 2 + . . . + a n − 2 x n − 2 F_1(x)=a_0+a_2x^2+...+a_{n-2}x^{n-2} F1(x)=a0+a2x2+...+an−2xn−2
F 2 ( x ) = a 1 x + a 3 x 3 + . . . + a n − 1 x n − 1 F_2(x)=a_1x+a_3x^3+...+a_{n-1}x^{n-1} F2(x)=a1x+a3x3+...+an−1xn−1
那么 F ( x ) = F 1 ( x 2 ) + x F 2 ( x 2 ) F(x)=F_1(x^2)+xF_2(x^2) F(x)=F1(x2)+xF2(x2)
假设 k < n 2 k<\frac{n}{2} k<2n
当我们带入 x = ω n k x=\omega_n^k x=ωnk的时候,用一下[性质2],式子是:
F ( ω n k ) = F 1 ( ω n 2 k ) + ω n k F 2 ( ω n 2 k ) F(\omega_n^k)=F_1(\omega_n^{2k})+\omega_n^kF_2(\omega_n^{2k}) F(ωnk)=F1(ωn2k)+ωnkF2(ωn2k)
利用[性质1],变形一下:
F ( ω n k ) = F 1 ( ω n 2 k ) + ω n k F 2 ( ω n 2 k ) F(\omega_n^k)=F_1(\omega_{\frac{n}{2}}^k)+\omega_n^kF_2(\omega_{\frac{n}{2}}^k) F(ωnk)=F1(ω2nk)+ωnkF2(ω2nk)
此时我们再带入 x = ω n k + n 2 x=\omega_n^{k+\frac{n}{2}} x=ωnk+2n,得到:
F ( ω n k + n 2 ) = F 1 ( ω n 2 ( k + n 2 ) ) + ω n k + n 2 F 2 ( ω n 2 ( k + n 2 ) ) F(\omega_n^{k+\frac{n}{2}})=F_1(\omega_n^{2(k+\frac{n}{2})})+\omega_n^{k+\frac{n}{2}}F_2(\omega_n^{2({k+\frac{n}{2}})}) F(ωnk+2n)=F1(ωn2(k+2n))+ωnk+2nF2(ωn2(k+2n))利用[性质2],变形一下,得: F ( ω n k + n 2 ) = F 1 ( ω n 2 k ⋅ ω n n ) + ω n k + n 2 F 2 ( ω n 2 k ⋅ ω n n ) F(\omega_n^{k+\frac{n}{2}})=F_1(\omega_n^{2k}\cdot\omega_n^n)+\omega_n^{k+\frac{n}{2}}F_2(\omega_n^{2k}\cdot\omega_n^n) F(ωnk+2n)=F1(ωn2k⋅ωnn)+ωnk+2nF2(ωn2k⋅ωnn) F ( ω n k + n 2 ) = F 1 ( ω n 2 k ) − ω n k F 2 ( ω n 2 k ) F(\omega_n^{k+\frac{n}{2}})=F_1(\omega_n^{2k})-\omega_n^kF_2(\omega_n^{2k}) F(ωnk+2n)=F1(ωn2k)−ωnkF2(ωn2k) F ( ω n k + n 2 ) = F 1 ( ω n 2 k ) − ω n k F 2 ( ω n 2 k ) F(\omega_n^{k+\frac{n}{2}})=F_1(\omega_{\frac{n}{2}}^k)-\omega_n^kF_2(\omega_{\frac{n}{2}}^k) F(ωnk+2n)=F1(ω2nk)−ωnkF2(ω2nk)
这两个式子有什么用呢?
实际上,我们在这里证明了每一个 F ( ω n k ) F(\omega_n^k) F(ωnk)都可以由某两个 F 1 / F 2 ( ω n 2 k ) F1/F2(\omega_{\frac{n}{2}}^k) F1/F2(ω2nk)得到,所以如果知道了所有 F 1 ( ω n 2 k ) 和 F 2 ( ω n 2 k ) F1(\omega_{\frac{n}{2}}^k)和F2(\omega_{\frac{n}{2}}^k) F1(ω2nk)和F2(ω2nk)的值,我们可以在O(n)时间内求出所有 F ( ω n k ) F(\omega_n^k) F(ωnk)的值。那么对于这个问题我们可以分而治之,也就是用分治递归解决。
我们如果要为这个过程再设计一种方法的话,那未免有点麻烦,我们可以尝试通过某张变换方式,是的IDFT也可以套用DFT的解决方法,这样就会方便很多。
首先考虑DFT的过程,可一转变为一个矩阵,如下:
[ ( ω n 0 ) 0 ( ω n 0 ) 1 ⋯ ( ω n 0 ) n − 1 ( ω n 1 ) 0 ( ω n 1 ) 1 ⋯ ( ω n 1 ) n − 1 ⋮ ⋮ ⋱ ⋮ ( ω n n − 1 ) 0 ( ω n n − 1 ) 1 ⋯ ( ω n n − 1 ) n − 1 ] [ a 0 a 1 ⋮ a n − 1 ] = [ F ( ω n 0 ) F ( ω n 1 ) ⋮ F ( ω n n − 1 ) ] \begin{bmatrix} (\omega_n^0)^0 & (\omega_n^0)^1 & \cdots & (\omega_n^0)^{n-1} \\ (\omega_n^1)^0 & (\omega_n^1)^1 & \cdots & (\omega_n^1)^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ (\omega_n^{n-1})^0 & (\omega_n^{n-1})^1 & \cdots & (\omega_n^{n-1})^{n-1} \end{bmatrix} \begin{bmatrix} a_0 \\ a_1 \\ \vdots \\ a_{n-1} \end{bmatrix} = \begin{bmatrix} F(\omega_n^0) \\ F(\omega_n^1) \\ \vdots \\ F(\omega_n^{n-1}) \end{bmatrix} ⎣⎢⎢⎢⎡(ωn0)0(ωn1)0⋮(ωnn−1)0(ωn0)1(ωn1)1⋮(ωnn−1)1⋯⋯⋱⋯(ωn0)n−1(ωn1)n−1⋮(ωnn−1)n−1⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡a0a1⋮an−1⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡F(ωn0)F(ωn1)⋮F(ωnn−1)⎦⎥⎥⎥⎤
我们假设最左边的系数矩阵为A,那么我们构造一个矩阵B,使得 B i , j = ( ω n − i ) j B_{i,j}=(\omega_n^{-i})^j Bi,j=(ωn−i)j,那么B矩阵就是这个样子:
B = [ ( ω n − 0 ) 0 ( ω n − 0 ) 1 ⋯ ( ω n − 0 ) n − 1 ( ω n − 1 ) 0 ( ω n − 1 ) 1 ⋯ ( ω n − 1 ) n − 1 ⋮ ⋮ ⋱ ⋮ ( ω n − ( n − 1 ) ) 0 ( ω n − ( n − 1 ) ) 1 ⋯ ( ω n − ( n − 1 ) ) n − 1 ] \mathbf B = \begin{bmatrix} (\omega_n^{-0})^0 & (\omega_n^{-0})^1 & \cdots & (\omega_n^{-0})^{n-1} \\ (\omega_n^{-1})^0 & (\omega_n^{-1})^1 & \cdots & (\omega_n^{-1})^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ (\omega_n^{-(n-1)})^0 & (\omega_n^{-(n-1)})^1 & \cdots & (\omega_n^{-(n-1)})^{n-1} \end{bmatrix} B=⎣⎢⎢⎢⎡(ωn−0)0(ωn−1)0⋮(ωn−(n−1))0(ωn−0)1(ωn−1)1⋮(ωn−(n−1))1⋯⋯⋱⋯(ωn−0)n−1(ωn−1)n−1⋮(ωn−(n−1))n−1⎦⎥⎥⎥⎤我们让A与B相乘,假设结果为C,那么可以写出:
C i , j = ∑ k = 0 n − 1 A i , k ⋅ B k , j C_{i,j}=\sum_{k=0}^{n-1}A_{i,k}\cdot B_{k,j} Ci,j=k=0∑n−1Ai,k⋅Bk,j
带入A和B的值,得到:
C i , j = ∑ k = 0 n − 1 ω n i k ⋅ ω n − k j C_{i,j}=\sum_{k=0}^{n-1}\omega_n^{ik}\cdot\omega_n^{-kj} Ci,j=k=0∑n−1ωnik⋅ωn−kj
C i , j = ∑ k = 0 n − 1 ω n k ( i − j ) C_{i,j}=\sum_{k=0}^{n-1}\omega_n^{k(i-j)} Ci,j=k=0∑n−1ωnk(i−j)
由此我们可以得出:
i=j时: C i , j = n C_{i,j}=n Ci,j=n
否则: C i , j = 1 − ( ω n i − j ) n 1 − ω n i − j = 0 C_{i,j}=\frac{1-(\omega_n^{i-j})^n}{1-\omega_n^{i-j}}=0 Ci,j=1−ωni−j1−(ωni−j)n=0
那么C矩阵实际上是单位矩阵的n倍!
那么我们就可以这么做:
[ a 0 a 1 ⋮ a n − 1 ] = 1 n [ ( ω n − 0 ) 0 ( ω n − 0 ) 1 ⋯ ( ω n − 0 ) n − 1 ( ω n − 1 ) 0 ( ω n − 1 ) 1 ⋯ ( ω n − 1 ) n − 1 ⋮ ⋮ ⋱ ⋮ ( ω n − ( n − 1 ) ) 0 ( ω n − ( n − 1 ) ) 1 ⋯ ( ω n − ( n − 1 ) ) n − 1 ] [ A ( ω n 0 ) A ( ω n 1 ) ⋮ A ( ω n n − 1 ) ] \begin{bmatrix} a_0 \\ a_1 \\ \vdots \\ a_{n-1} \end{bmatrix} = \frac{1}{n} \begin{bmatrix} (\omega_n^{-0})^0 & (\omega_n^{-0})^1 & \cdots & (\omega_n^{-0})^{n-1} \\ (\omega_n^{-1})^0 & (\omega_n^{-1})^1 & \cdots & (\omega_n^{-1})^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ (\omega_n^{-(n-1)})^0 & (\omega_n^{-(n-1)})^1 & \cdots & (\omega_n^{-(n-1)})^{n-1} \end{bmatrix} \begin{bmatrix} A(\omega_n^0) \\ A(\omega_n^1) \\ \vdots \\ A(\omega_n^{n-1}) \end{bmatrix} ⎣⎢⎢⎢⎡a0a1⋮an−1⎦⎥⎥⎥⎤=n1⎣⎢⎢⎢⎡(ωn−0)0(ωn−1)0⋮(ωn−(n−1))0(ωn−0)1(ωn−1)1⋮(ωn−(n−1))1⋯⋯⋱⋯(ωn−0)n−1(ωn−1)n−1⋮(ωn−(n−1))n−1⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡A(ωn0)A(ωn1)⋮A(ωnn−1)⎦⎥⎥⎥⎤
然而这就可以直接再做一遍FFT来解决。
模板题:UOJ 34
#include
using namespace std;
const int maxn=100005;
int n,m,bitn;
const double Pi=acos(-1.0);
struct comx{
double x,y;
comx (double x1=0,double y1=0){x=x1,y=y1;}
comx operator +(comx b){return comx(x+b.x,y+b.y);}
comx operator -(comx b){return comx(x-b.x,y-b.y);}
comx operator *(comx b){return comx(x*b.x-y*b.y,x*b.y+y*b.x);}
}a[maxn],b[maxn];
void FFT(int lim,comx *a,int p){
if (lim==1) return;
comx a1[lim>>1],a2[lim>>1];
for (int i=0;i<=lim;i+=2) a1[i>>1]=a[i],a2[i>>1]=a[i+1];
FFT(lim>>1,a1,p); FFT(lim>>1,a2,p);
comx omn=comx(cos(2.0*Pi/lim),1.0*p*sin(2.0*Pi/lim)),now=comx(1.0,0.0);
for (int i=0;i<(lim>>1);i++){
a[i]=a1[i]+now*a2[i];
a[i+(lim>>1)]=a1[i]-now*a2[i];
now=now*omn;
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=0;i<=n;i++) scanf("%lf",&a[i].x);
for (int i=0;i<=m;i++) scanf("%lf",&b[i].x);
bitn=1; while (bitn<=(n+m)) bitn<<=1;
FFT(bitn,a,1); FFT(bitn,b,1);
for (int i=0;i<=bitn;i++) a[i]=a[i]*b[i];
FFT(bitn,a,-1);
for (int i=0;i<=n+m;i++) printf("%.0lf ",a[i].x/(1.0*bitn));
printf("\n");
return 0;
}
#include
using namespace std;
const int maxn=(1<<20)+5;
const double Pi=acos(-1.0);
int n,m,limn,R[maxn];
struct C{
double x,y;
C(double xx=0,double yy=0){x=xx,y=yy;}
C operator +(C b) {return C(x+b.x,y+b.y);}
C operator -(C b) {return C(x-b.x,y-b.y);}
C operator *(C b) {return C(x*b.x-y*b.y,x*b.y+b.x*y);}
}a[maxn],b[maxn],c[maxn],w[maxn];
void FFT(C a[],int lim){
for (int i=0;i<lim;i++) if (R[i]>i) swap(a[R[i]],a[i]);
for (int t=lim>>1,d=1;d<lim;d<<=1,t>>=1)
for (int i=0;i<lim;i+=(d<<1))
for (int j=0;j<d;j++){
C p=w[t*j]*a[i+j+d];
a[i+j+d]=a[i+j]-p,a[i+j]=a[i+j]+p;
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=0;i<=n;i++) scanf("%lf",&a[i].x);
for (int i=0;i<=m;i++) scanf("%lf",&b[i].x);
int L; for (limn=1,L=0;limn<=n+m;limn<<=1,L++);
for (int i=0;i<limn;i++)
R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)),w[i]=C(cos(2.0*Pi*i/limn),sin(2.0*Pi*i/limn));
FFT(a,limn); FFT(b,limn);
for (int i=0;i<limn;i++)
c[i]=a[i]*b[i],w[i].y=-w[i].y;
FFT(c,limn);
for (int i=0;i<=n+m;i++) printf("%d ",(int)(c[i].x/limn+0.5));
printf("\n");
return 0;
}
#include
using namespace std;
const int maxn=(1<<17)+5;
const double Pi=acos(-1.0);
int n,m,limn,R[maxn],ans[maxn];
char s[maxn];
struct comx{
double x,y;
comx(double xx=0,double yy=0){x=xx,y=yy;}
comx operator +(const comx b){return comx(x+b.x,y+b.y);}
comx operator -(const comx b){return comx(x-b.x,y-b.y);}
comx operator *(const comx b){return comx(x*b.x-y*b.y,x*b.y+b.x*y);}
}a[maxn],b[maxn],w[maxn];
void exc(comx *a,int len){for (int i=0;i<=len;i++) a[len-i].x=s[i]-'0';}
void fft(comx *a,int lim){
for (int i=0;i<lim;i++) if (R[i]>i) swap(a[R[i]],a[i]);
for (int t=lim>>1,d=1;d<lim;d<<=1,t>>=1)
for (int i=0;i<lim;i+=(d<<1))
for (int j=0;j<d;j++){
comx p=w[t*j]*a[i+j+d];
a[i+j+d]=a[i+j]-p,a[i+j]=a[i+j]+p;
}
}
int main(){
while (~scanf("%s",s)){
for (int i=0;i<=(1<<17);i++) a[i].x=a[i].y=0.0,b[i].x=b[i].y=0.0,R[i]=0,w[i].x=w[i].y=0.0;
n=strlen(s); exc(a,--n);
scanf("%s",s);
m=strlen(s); exc(b,--m);
int l=0; limn=1;
while (limn<=n+m) limn<<=1,l++;
for (int i=0;i<limn;i++){
R[i]=((R[i>>1]>>1)|((i&1)<<(l-1)));
w[i]=comx(cos(2.0*Pi/limn*i),sin(2.0*Pi/limn*i));
}
fft(a,limn); fft(b,limn);
for (int i=0;i<limn;i++) a[i]=a[i]*b[i],w[i].y=-w[i].y;
fft(a,limn);
memset(ans,0,sizeof(ans));
for (int i=0;i<limn;i++) ans[i]=(int)(a[i].x/limn+0.5);
for (int i=0;i<=n+m+1;i++) ans[i+1]+=ans[i]/10,ans[i]%=10;
int maxx=0;
for (int i=0;i<=n+m+1;i++) if (ans[i]>0) maxx=i;
for (int i=maxx;i>=0;i--) printf("%d",ans[i]); printf("\n");
}
return 0;
}
#include
#define ll long long
using namespace std;
const int maxn=(1<<18)+5;
const double Pi=acos(-1.0);
int n,m,limn,R[maxn];
char S[maxn],T[maxn];
struct comx{
double x,y;
comx(double xx=0,double yy=0){x=xx,y=yy;}
comx operator +(const comx b){return comx(x+b.x,y+b.y);}
comx operator -(const comx b){return comx(x-b.x,y-b.y);}
comx operator *(const comx b){return comx(x*b.x-y*b.y,x*b.y+y*b.x);}
}a[maxn],b[maxn],c[maxn],d[maxn],w[maxn];
double s;
void pre(){
int L=0; limn=1; while (limn<=n+m) limn<<=1,L++;
for (int i=0;i<limn;i++){
R[i]=((R[i>>1]>>1)|((i&1)<<(L-1)));
w[i]=comx(cos(2*Pi/limn*i),sin(2*Pi/limn*i));
}
}
void FFT(comx *a,int lim){
for (int i=0;i<lim;i++) if (R[i]>i) swap(a[R[i]],a[i]);
for (int t=lim>>1,d=1;d<lim;d<<=1,t>>=1)
for (int i=0;i<lim;i+=(d<<1))
for (int j=0;j<d;j++){
comx p=w[t*j]*a[i+j+d];
a[i+j+d]=a[i+j]-p,a[i+j]=a[i+j]+p;
}
}
void doit(comx *p,comx *q){
FFT(p,limn); FFT(q,limn);
for (int i=0;i<limn;i++) p[i]=p[i]*q[i],w[i].y=-w[i].y;
FFT(p,limn);
for (int i=0;i<limn;i++) w[i].y=-w[i].y,p[i].x/=limn;
}
ll cal(double x){return (ll)(x+0.5);}
int main(){
scanf("%s",S); scanf("%s",T);
n=strlen(S)-1; m=strlen(T)-1;
for (int i=0;i<=n;i++) c[i].x=S[i]-'a'+1;
for (int i=0;i<=m;i++)
if (T[i]!='?') b[m-i].x=T[i]-'a'+1; else b[m-i].x=0;
for (int i=0;i<=n;i++) a[i].x=c[i].x*c[i].x;
for (int i=0;i<=m;i++) d[i].x=b[i].x*b[i].x,s+=b[i].x*b[i].x*b[i].x;
pre(); doit(a,b); doit(c,d);
int ans=0;
for (int i=m;i<=n;i++)
if (cal(a[i].x)-2*cal(c[i].x)+cal(s)==0) ans++;
printf("%d\n",ans);
for (int i=m;i<=n;i++)
if (cal(a[i].x)-2*cal(c[i].x)+cal(s)==0) printf("%d\n",i-m);
return 0;
}