FFT学习参考这两篇博客,很详细,结合这看,互补。
很大一部分题目需要构造多项式相乘来进行计数问题。
1. HDU 1402 A * B Problem Plus
把A和B分别当作多项式的系数。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> using namespace std; const double PI = acos(-1.0); const int maxn = 5e4+50; struct Complex { double real,image; ///实部和虚部 Complex(double _real,double _image) { real = _real; image = _image; } Complex(){} }; Complex operator + (const Complex &c1,const Complex &c2) { return Complex(c1.real+c2.real,c1.image+c2.image); } Complex operator - (const Complex &c1,const Complex &c2) { return Complex(c1.real-c2.real,c1.image-c2.image); } Complex operator * (const Complex &c1,const Complex &c2) { return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real); } int rev(int id,int len) { int ret = 0; for(int i=0;(1<<i)<len;i++) { ret <<= 1; if(id & (1<<i)) ret |= 1; } return ret; } Complex A[135000]; void FFT(Complex* a,int len,int DFT) ///对a进行DFT或者逆DFT,结果存在a当中 { for(int i = 0; i < len; i++) A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储 for(int s = 1; (1 << s)<= len; s++) { int m = (1 << s); Complex wm = Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m)); ///主n次单位根 for(int k = 0; k < len; k += m) { Complex w = Complex(1, 0); ///旋转因子 for(int j = 0; j < (m >> 1); j++) { Complex t = w * A[k + j + (m >> 1)]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + (m >> 1)] = u - t; w = w * wm; } } } if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len; for(int i = 0; i < len; i++) a[i] = A[i]; return; } char coA[maxn],coB[maxn]; ///把每一位作为系数 ///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<16)<2*n-2<(1<<17) Complex a[135000],b[135000]; int ans[135000]; int main() { while(scanf("%s",coA)!=EOF) { int lenA = strlen(coA); int mia = 0; while((1<<mia)<lenA) mia++; ///2^mia>=lenA scanf("%s",coB); int lenB = strlen(coB); int mib = 0; while((1<<mib)<lenB) mib++; int len(1<<(max(mia,mib)+1)); for(int i=0;i<len;i++) { if(i<lenA) a[i] = Complex(coA[lenA-i-1]-'0',0); ///表示系数,A数组从左往右存了进去 2000 --> 0002 else a[i] = Complex(0,0); if(i<lenB) b[i] = Complex(coB[lenB-i-1]-'0',0); else b[i] = Complex(0,0); } ///求A和B的点值表达式 FFT(a, len, 1); FFT(b, len, 1); for(int i = 0; i < len; i++) a[i] = a[i] * b[i]; ///求C的点值 FFT(a, len, -1); ///逆DFT得到系数 for(int i = 0; i < len; i++) ans[i] = (int)(a[i].real + 0.5); ///四舍五入 for(int i = 0; i <len - 1; i++) { ans[i + 1] += ans[i] / 10; ans[i] %= 10; } bool flag = 0; for(int i = len - 1; i >= 0; i--) ///防止出现前导0 { if(ans[i]) printf("%d",ans[i]), flag = 1; else if(flag || i == 0) printf("0"); } puts(""); } return 0; }
2.Golf Bot
从K中选两个或者1个,选两个相当于指数相加,比如x^5与x^1相乘,相当于5和1都被选了,直接相加,所以把k中的数字当成系数1,没出现过当成系数0。
对于选1个的可以把x^0的系数设为1.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <map> using namespace std; const double PI = acos(-1.0); const int maxn = 2e5+50; struct Complex { double real,image; ///实部和虚部 Complex(double _real,double _image) { real = _real; image = _image; } Complex(){} }; Complex operator + (const Complex &c1,const Complex &c2) { return Complex(c1.real+c2.real,c1.image+c2.image); } Complex operator - (const Complex &c1,const Complex &c2) { return Complex(c1.real-c2.real,c1.image-c2.image); } Complex operator * (const Complex &c1,const Complex &c2) { return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real); } int rev(int id,int len) { int ret = 0; for(int i=0;(1<<i)<len;i++) { ret <<= 1; if(id & (1<<i)) ret |= 1; } return ret; } Complex A[540000]; void FFT(Complex* a,int len,int DFT) ///对a进行DFT或者逆DFT,结果存在a当中 { for(int i = 0; i < len; i++) A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储 for(int s = 1; (1 << s)<= len; s++) { int m = (1 << s); Complex wm = Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m)); ///主n次单位根 for(int k = 0; k < len; k += m) { Complex w = Complex(1, 0); ///旋转因子 for(int j = 0; j < (m >> 1); j++) { Complex t = w * A[k + j + (m >> 1)]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + (m >> 1)] = u - t; w = w * wm; } } } if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len; for(int i = 0; i < len; i++) a[i] = A[i]; return; } int coA[maxn]; ///把每一位作为系数 ///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<16)<2*n-2<(1<<17) Complex a[540000]; int ans[540000]; int num[maxn]; int main() { // freopen("1.txt","r",stdin); int n,m; while(scanf("%d", &n)!=EOF) { memset(coA, 0, sizeof(coA)); int maxv = 0; for(int i = 0; i < n; i++) { int x; scanf("%d", &x); coA[x] = 1; maxv = max(maxv, x); } coA[0] = 1; int mia = 0; while((1 << mia) < maxv) mia++; int len = (1 << (mia + 1)); for(int i = 0; i < len; i++) { if(i <= maxv && coA[i]) a[i] = Complex(1,0); else a[i] = Complex(0, 0); } ///求A和B的点值表达式 FFT(a, len, 1); for(int i = 0; i < len; i++) a[i] = a[i] * a[i]; ///求C的点值 FFT(a, len, -1); ///逆DFT得到系数 memset(ans,0,sizeof(ans)); for(int i = 0; i < len; i++) ans[i] = (int)(a[i].real + 0.5); ///四舍五入 int cnt = 0; scanf("%d", &m); for(int i = 0; i < m; i++) { int x; scanf("%d", &x); if(ans[x]) cnt++; } printf("%d\n",cnt); } return 0; }
3.HDU - 4609 3-idiots
之前做过给定n个数,任意两个数求和,求总共有多少个不同的,每个数出现过它的系数标记为1,这样FFT后,某个系数不等于0就代表这个数出现过。
这个题,算两个边求和,然后枚举另一条边,算能构成三角形的次数。
以下以1 3 3 4为例。
把长度归为指数后,次数归为系数后,指数从大到小变成{1,2,0,1,0},自己卷积自己{1,2,0,1,0} * {1,2,0,1,0}
得到{1,4,4,2,4,0,1,0,0},代表每一个和出现的次数,用ans数组表示
因为构成三角形,不能两次取同一根棍,所以减去两次用同一根棍的
for(int i = 0; i < n; i++) ans[a[i] + a[i]]--;
还有因为卷积的时候,1 4 和 4 1有了顺序,我们在选三角形的时候是没有顺序的,所以要/2
for(int i = 0; i <= len; i++) ans[i] /= 2;
为了后面统计方便,我们求个ans的前缀和,得到sum数组。
我们对所有边排个序,然后枚举一条边a[i],我们假设它是所选三角形里面长度最大的,那么我们能够成三角形的条件之一是两边之和 > a[i],所以 cnt[i] += sum[len] - sum[a[i]].
但是这里面的和有不符合条件的:
1.一大一小,cnt -= (n - i - 1) * i;
2.一个是它本身,另一个是其他,cnt -= (n-1);
3.两个都是大于的,C(n - i - 1, 2)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <map> using namespace std; typedef long long ll; const double PI = acos(-1.0); const int maxn = (1 << 18) + 50; struct Complex { double real,image; ///实部和虚部 Complex(double _real,double _image) { real = _real; image = _image; } Complex(){} }; Complex operator + (const Complex &c1,const Complex &c2) { return Complex(c1.real+c2.real,c1.image+c2.image); } Complex operator - (const Complex &c1,const Complex &c2) { return Complex(c1.real-c2.real,c1.image-c2.image); } Complex operator * (const Complex &c1,const Complex &c2) { return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real); } int rev(int id,int len) { int ret = 0; for(int i=0;(1<<i)<len;i++) { ret <<= 1; if(id & (1<<i)) ret |= 1; } return ret; } Complex A[maxn]; void FFT(Complex* a,int len,int DFT) ///对a进行DFT或者逆DFT,结果存在a当中 { for(int i = 0; i < len; i++) A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储 for(int s = 1; (1 << s)<= len; s++) { int m = (1 << s); Complex wm = Complex(cos(DFT*2*PI/m),sin(DFT*2*PI/m)); ///主n次单位根 for(int k = 0; k < len; k += m) { Complex w = Complex(1, 0); ///旋转因子 for(int j = 0; j < (m >> 1); j++) { Complex t = w * A[k + j + (m >> 1)]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + (m >> 1)] = u - t; w = w * wm; } } } if(DFT == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len; for(int i = 0; i < len; i++) a[i] = A[i]; return; } int coA[maxn]; ///把每一位作为系数 ///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18) Complex a[maxn]; ll sum[maxn]; ll ans[maxn]; int bran[maxn]; int main() { // freopen("1.txt","r",stdin); int T; scanf("%d", &T); int n; while(T--) { scanf("%d", &n); memset(coA, 0, sizeof(coA)); int maxv = 0; for(int i = 0; i < n; i++) { int x; scanf("%d", &x); bran[i] = x; coA[x]++; } sort(bran, bran + n); int mia = 0; while((1 << mia) < (bran[n - 1] + 1) ) mia++; int len = (1 << (mia + 1)); for(int i = 0; i < len; i++) { if(coA[i]) a[i] = Complex(coA[i], 0); else a[i] = Complex(0, 0); } ///求A和B的点值表达式 FFT(a, len, 1); for(int i = 0; i < len; i++) a[i] = a[i] * a[i]; ///求C的点值 FFT(a, len, -1); ///逆DFT得到系数 memset(sum, 0, sizeof(sum)); for(int i = 0; i < len; i++) ans[i] = (ll)(a[i].real + 0.5); ///四舍五入 for(int i = 0; i < n; i++) ans[bran[i] + bran[i]]--; for(int i = 1; i < len; i++) { ans[i] /= 2LL; sum[i] = sum[i - 1] + ans[i]; } ll cnt = 0; for(int i = 0; i < n; i++) { cnt += sum[len - 1] - sum[bran[i]]; cnt -= (ll)(n - i - 1) * i; cnt -= (ll)(n - 1); cnt -= (ll)(n - i - 1) * (n - i - 2) / 2; } ll all = (ll)n * (n - 1) * (n - 2) / 6LL; printf("%.7f\n", (double)cnt / all); } return 0; }
4. K-neighbor substrings
字符串的这一类型变换最近已经碰到四道了,以这道题为例,学习这一类算法。
一开始看题解,好多人都说,要把B串翻转,我一直在想为什么翻转
看完这两个文章后,
https://blog.csdn.net/q582116859/article/details/77248970
https://www.zhihu.com/question/22298352
我发现我一直纠结与卷积代码的实现,而忽略了卷积数学公式的书写。
以知乎中掷两颗色子,和为4为例。
看完这个例子就明白了为啥要逆序,因为B串是逆序比较的。
最后得到的卷子公式
4 - m + m = 4,代表两个因变量之和是4,
到我们这道题,就是
这个上面有点问题,X是从0~n-1,Y是0~m-1.
最后以i开头的字符比较,Hamming距离都累加到 i + m - 1上了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <map> #include <set> using namespace std; typedef unsigned long long ll; const double PI = acos(-1.0); const int maxn = (1 << 18) + 50; struct Complex { double real,image; ///实部和虚部 Complex(double _real,double _image) { real = _real; image = _image; } Complex(){} }; Complex operator + (const Complex &c1,const Complex &c2) { return Complex(c1.real+c2.real,c1.image+c2.image); } Complex operator - (const Complex &c1,const Complex &c2) { return Complex(c1.real-c2.real,c1.image-c2.image); } Complex operator * (const Complex &c1,const Complex &c2) { return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real); } int rev(int id,int len) { int ret = 0; for(int i=0;(1<<i)<len;i++) { ret <<= 1; if(id & (1<<i)) ret |= 1; } return ret; } Complex A[maxn]; void DFT(Complex* a,int len,int flag) ///对a进行DFT或者逆DFT,结果存在a当中 { for(int i = 0; i < len; i++) A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储 for(int s = 1; (1 << s)<= len; s++) { int m = (1 << s); Complex wm = Complex(cos(flag*2*PI/m),sin(flag*2*PI/m)); ///主n次单位根 for(int k = 0; k < len; k += m) { Complex w = Complex(1, 0); ///旋转因子 for(int j = 0; j < (m >> 1); j++) { Complex t = w * A[k + j + (m >> 1)]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + (m >> 1)] = u - t; w = w * wm; } } } if(flag == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len; for(int i = 0; i < len; i++) a[i] = A[i]; return; } ///把每一位作为系数 ///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18) Complex a[maxn]; Complex b[maxn]; int num[maxn]; void FFT(int len) { DFT(a, len, 1); DFT(b, len, 1); for(int i = 0; i < len; i++) a[i] = a[i] * b[i]; DFT(a, len, -1); for(int i = 0; i < len; i++) num[i] += (int)(a[i].real + 0.5); } int coA[maxn]; char s1[maxn]; char s2[maxn]; #define pow Pow ll pow[maxn]; ll Hash[maxn]; ll magic = 3; ///hash利用自然数溢出来取模 set<ll> st; int main() { int K, kase = 0; pow[0] = 1; for(int i = 1; i < maxn; i++) pow[i] = pow[i - 1] * magic; while(scanf("%d", &K) && K != -1) { scanf("%s %s", s1, s2); int la = strlen(s1); int lb = strlen(s2); printf("Case %d: ", ++kase); if(la < lb) { puts("0"); continue; } int mi = 0; while((1 << mi) < (max(la, lb) + 1)) mi++; int len = (1 << (mi + 1)); for(int i = 0; i < lb / 2; i++) swap(s2[i], s2[lb - i - 1]); ///A串的a取1,B串的b取1 memset(num, 0, sizeof(num)); for(int i = 0; i < len; i++) { if(i < la) a[i] = Complex((s1[i] == 'a' ? 1 : 0), 0); else a[i] = Complex(0, 0); if(i < lb) b[i] = Complex((s2[i] == 'b' ? 1 : 0), 0); else b[i] = Complex(0, 0); } FFT(len); for(int i = 0; i < len; i++) { // printf("%d ", num[i]); } for(int i = 0; i < len; i++) { if(i < la) a[i] = Complex((s1[i] == 'b' ? 1 : 0), 0); else a[i] = Complex(0, 0); if(i < lb) b[i] = Complex((s2[i] == 'a' ? 1 : 0), 0); else b[i] = Complex(0, 0); } FFT(len); Hash[0] = 0; for(int i = 0; i < la; i++) Hash[i + 1] = Hash[i] * magic + (ll)(s1[i] - 'a' + 1); int cnt = 0; st.clear(); for(int i = lb - 1; i < la; i++) { // printf("%d ",num[i]); ll now = Hash[i + 1] - Hash[i - lb + 1] * pow[lb]; if(st.count(now)) continue; if(num[i] <= K) { st.insert(now); cnt++; } } printf("%d\n", cnt); } return 0; } Code
5. Rock Paper Scissors Lizard Spock.
会了上面的字符串的题,这种类型的就差不多会了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <map> #include <set> using namespace std; typedef unsigned long long ll; const double PI = acos(-1.0); const int maxn = (1 << 21) + 50; struct Complex { double real,image; ///实部和虚部 Complex(double _real,double _image) { real = _real; image = _image; } Complex(){} }; Complex operator + (const Complex &c1,const Complex &c2) { return Complex(c1.real+c2.real,c1.image+c2.image); } Complex operator - (const Complex &c1,const Complex &c2) { return Complex(c1.real-c2.real,c1.image-c2.image); } Complex operator * (const Complex &c1,const Complex &c2) { return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real); } int rev(int id,int len) { int ret = 0; for(int i=0;(1<<i)<len;i++) { ret <<= 1; if(id & (1<<i)) ret |= 1; } return ret; } Complex A[maxn]; void DFT(Complex* a,int len,int flag) ///对a进行DFT或者逆DFT,结果存在a当中 { for(int i = 0; i < len; i++) A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储 for(int s = 1; (1 << s)<= len; s++) { int m = (1 << s); Complex wm = Complex(cos(flag*2*PI/m),sin(flag*2*PI/m)); ///主n次单位根 for(int k = 0; k < len; k += m) { Complex w = Complex(1, 0); ///旋转因子 for(int j = 0; j < (m >> 1); j++) { Complex t = w * A[k + j + (m >> 1)]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + (m >> 1)] = u - t; w = w * wm; } } } if(flag == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len; for(int i = 0; i < len; i++) a[i] = A[i]; return; } ///把每一位作为系数 ///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18) Complex a[maxn]; Complex b[maxn]; int num[maxn]; void FFT(int len) { DFT(a, len, 1); DFT(b, len, 1); for(int i = 0; i < len; i++) a[i] = a[i] * b[i]; DFT(a, len, -1); for(int i = 0; i < len; i++) num[i] += (int)(a[i].real + 0.5); } char s1[maxn]; char s2[maxn]; int la, lb, len; void calc(char win, char lose) { for(int i = 0; i < len; i++) { if(i < la) a[i] = Complex((s1[i] == lose ? 1 : 0), 0); else a[i] = Complex(0, 0); if(i < lb) b[i] = Complex((s2[i] == win ? 1 : 0), 0); else b[i] = Complex(0, 0); } FFT(len); } int main() { scanf("%s %s", s1, s2); la = strlen(s1); lb = strlen(s2); for(int i = 0; i < lb / 2; i++) swap(s2[i], s2[lb - 1 - i]); int mi = 0; while((1 << mi) < (max(la, lb) + 1)) mi++; len = (1 << (mi + 1)); ///A串的a取1,B串的b取1 memset(num, 0, sizeof(num)); calc('S', 'P'); calc('P', 'R'); calc('R', 'L'); calc('L', 'K'); calc('K', 'S'); calc('S', 'L'); calc('L', 'P'); calc('P', 'K'); calc('K', 'R'); calc('R', 'S'); int ans = 0; for(int i = lb - 1; i < la; i++) { ans = max(ans, num[i]); } printf("%d\n", ans); return 0; }
6.Educational Codeforces Round 40 Yet Another String Matching Problem
FFT + 并查集。
这个字符串处理的比前面的难了一些。
首先处理如何计算两个相同长度的串。
相同位的字母间连边,最后得到这样的图:
找到了一个好用的画图软件,再也不用画图板了。。。https://csacademy.com/app/graph_editor/
那最后的距离就是不同的节点个数 - 联通块个数 = 6 - 2 = 4,为什么呢
首先,我们先要明白,一个联通块的节点,最后都会变成同一个字母,
比如全变成e。那一个联通块内不同的节点就是总个数 - 1,所以总的就是 总个数 - 联通块个数。
现在再来考虑题目:
我们把每一次比较当成一个无向图,那么最多有 n - m + 1个无向图。
下面就像正常的FFT那样计算,比如 上面的串中'a'设为1,下面的串'b'设为1,FFT后,
系数为1的点,代表此无向图中,有a --> b这条边,因为只有a~f这6个字母,所以每个无向图的节点只有6个,直接跑并查集就可以了。
时间复杂度O(30nlogn)。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <map> #include <set> using namespace std; typedef unsigned long long ll; const double PI = acos(-1.0); const int maxn = (1 << 18) + 50; struct Complex { double real,image; ///实部和虚部 Complex(double _real,double _image) { real = _real; image = _image; } Complex(){} }; Complex operator + (const Complex &c1,const Complex &c2) { return Complex(c1.real+c2.real,c1.image+c2.image); } Complex operator - (const Complex &c1,const Complex &c2) { return Complex(c1.real-c2.real,c1.image-c2.image); } Complex operator * (const Complex &c1,const Complex &c2) { return Complex(c1.real*c2.real-c1.image*c2.image, c1.real*c2.image + c1.image * c2.real); } int rev(int id,int len) { int ret = 0; for(int i=0;(1<<i)<len;i++) { ret <<= 1; if(id & (1<<i)) ret |= 1; } return ret; } Complex A[maxn]; void DFT(Complex* a,int len,int flag) ///对a进行DFT或者逆DFT,结果存在a当中 { for(int i = 0; i < len; i++) A[rev(i,len)] = a[i]; ///按其在叶子节点中的顺序存储 for(int s = 1; (1 << s)<= len; s++) { int m = (1 << s); Complex wm = Complex(cos(flag*2*PI/m),sin(flag*2*PI/m)); ///主n次单位根 for(int k = 0; k < len; k += m) { Complex w = Complex(1, 0); ///旋转因子 for(int j = 0; j < (m >> 1); j++) { Complex t = w * A[k + j + (m >> 1)]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + (m >> 1)] = u - t; w = w * wm; } } } if(flag == -1) for(int i = 0; i < len; i++) A[i].real /= len, A[i].image /= len; for(int i = 0; i < len; i++) a[i] = A[i]; return; } ///把每一位作为系数 ///乘积后次数最大为2 * n - 2,转换成2的k次幂,(1<<17)<2*n-2<(1<<18) Complex a[maxn]; Complex b[maxn]; int num[maxn]; void FFT(int len) { DFT(a, len, 1); DFT(b, len, 1); for(int i = 0; i < len; i++) a[i] = a[i] * b[i]; DFT(a, len, -1); for(int i = 0; i < len; i++) num[i] = (int)(a[i].real + 0.5); } char s1[maxn]; char s2[maxn]; int la, lb, len; int pre[maxn][7]; void calc(char l, char r) { for(int i = 0; i < len; i++) { if(i < la) a[i] = Complex((s1[i] == l ? 1 : 0), 0); else a[i] = Complex(0, 0); if(i < lb) b[i] = Complex((s2[i] == r ? 1 : 0), 0); else b[i] = Complex(0, 0); } FFT(len); } int Find(int i, int x) { if(x == pre[i][x]) return x; return pre[i][x] = Find(i, pre[i][x]); } set<int> st[maxn]; int main() { scanf("%s %s", s1, s2); la = strlen(s1); lb = strlen(s2); for(int i = 0; i < lb / 2; i++) swap(s2[i], s2[lb - 1 - i]); int mi = 0; while((1 << mi) < (max(la, lb) + 1)) mi++; len = (1 << (mi + 1)); ///A串的a取1,B串的b取1 for(int i = lb - 1; i < la; i++) { for(int j = 0; j < 6; j++) { pre[i][j] = j; } } memset(num, 0, sizeof(num)); for(int i = 0; i < 6; i++) { for(int j = 0; j < 6; j++) { if(i == 0 && j == 3) { int ccc = 0; } int u = i, v = j; calc(u + 'a', v + 'a'); for(int k = lb - 1; k < la; k++) { if(num[k]) ///代表有边 { int x = Find(k, u); int y = Find(k, v); pre[k][x] = pre[k][y]; st[k].insert(u); st[k].insert(v); } } } } for(int i = lb - 1; i < la; i++) { int cnt = 0; for(int j = 0; j < 6; j++) { if(pre[i][j] == j && (st[i].count(j))) cnt++; } printf("%d ", st[i].size() - cnt); } return 0; }