北京集训TEST16——图片加密(fft+kmp)
题目:
Description
CJB天天要跟妹子聊天,可是他对微信的加密算法表示担心:“微信这种加密算法,早就过时了,我发明的加密算法早已风靡全球,安全性天下第一!”
CJB是这样加密的:设CJB想加密的信息有 m 个字节。首先,从网上抓来一张 n(n≥m) 个字节的图片,分析里面的每个字节(byte)。每个字节有8位(bit)二进制数字。他想替换掉某些字节中最低位的二进制数字,使得这张图片中,连续 m 个字节恰为他想加密的信息。这样,图片看起来没什么区别,却包含了意味深长的信息。
很显然,不是所有的图片都能让CJB加密他那 m 个字节的信息。他想请你帮忙写个程序判断这张图片是否能加密指定的信息。如果可以加密,则CJB要求改变最少的字节数。如果仍有多种解,他希望信息在图片中的位置越前越好。
Input
第一行包含两个整数 n,m(1≤n,m≤250000) ,表示图片的大小和信息的大小。
第二行表示图片的内容。
第三行表示信息的内容。
所有内容都以二进制字节的形式给出。每个字节有8位,最左边是最高位,最右边为最低位。
Output
如果这张图片不能加密这些信息,输出No
否则第一行输出Yes,第二行输出两个整数:最少修改的字节数,加密信息的起始位置。如果有多组解,要求加密信息的起始位置尽量前。
Sample Input
【样例输入1】
3 2
11110001 11110001 11110000
11110000 11110000
【样例输入2】
3 1
11110000 11110001 11110000
11110000
Sample Output
【样例输出1】
Yes
1 2
【样例输出2】
Yes
0 1
HINT
【样例解释】
图片有3个字节,信息有2个字节。
图片前两个字节可以匹配信息,需要改变两个字节中的最低位。
图片后两个字节可以匹配信息,只需要改变一个字节(第二个字节)中的最低位,信息在图片中的起始位置为第2个字节。
【数据范围与约定】
对于10%的数据, n,m≤500
对于40%的数据, n,m≤5000
对于70%的数据, n,m≤105
对于所有数据, 1≤n,m≤2.5×105
题解:
本题解法:KMP+FFT。
首先,先去掉最低位,跑一次KMP,记录所有可以加密为信息的起始位置。
然后我们只保留最低位,存入a, b数组里。我们很容易发现,从第 i 个字节作为起始位置计算的话,答案为
我们可以把b反过来,即可变成卷积形式。
其中,异或可以拆成两个乘法操作相加,即:a xor b=(a∗!b)+(!a∗b) ,那么我们做两次FFT就可以解决了~
心得:
又一道fft求卷积题····表示做了这么多次fft终于有点感觉了,其实以后做题要是推出了带∑的式子应该快点想到fft的··只要想办法把里面的运算变成相乘的就行了···
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=250005; const double pi=acos(-1.0); int a[N],b[N],c[N],d[N]; int n,m; int able[N],tot,rev[N]; int nxt[N],k,xj; int ansa[N],ansb[N]; char s[N]; struct Complex { double r,i; Complex (double r=0,double i=0):r(r),i(i) {} } A[N],C[N],B[N],D[N]; Complex operator + (Complex &a,Complex &b) { return Complex(a.r+b.r,a.i+b.i); } Complex operator - (Complex &a,Complex &b) { return Complex(a.r-b.r,a.i-b.i); } Complex operator * (Complex &a,Complex &b) { return Complex(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r); } Complex operator / (Complex &a,double x) { return Complex(a.r/x,a.i/x); } inline void kmp() { for (int i=2,j=0;i<=m;++i) { while (j && b[i]!=b[j+1]) j=nxt[j]; nxt[i]=(b[i]==b[j+1]?++j:j); } for (int i=1,j=0;i<=n;++i) { while (j && a[i]!=b[j+1]) j=nxt[j]; if (a[i]==b[j+1]) ++j; if (j==m) able[++tot]=i-m+1,j=nxt[j]; } } inline void pre() { for (k=1,xj=0;k<=(n<<1);k<<=1,++xj); for (int i=0;i<k;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(xj-1)); } void fft(Complex a[],int len,int op=1) { for (int i=0;i<len;++i) if (i<rev[i]) swap(a[i],a[rev[i]]); for (int i=2;i<=len;i<<=1) { Complex wn(cos(pi/i),sin(pi/i)*op); for (int j=0;j<len;j+=i) { Complex w(1); for (int k=j;k<j+i/2;++k,w=w*wn) { Complex u=a[k],v=a[k+i/2]*w; a[k]=u+v,a[k+i/2]=u-v; } } } if (op==-1) { for (int i=0;i<len;++i) a[i]=a[i]/len; } } int main() { //freopen("a.in","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) c[i]=a[i]%10; for(int i=1;i<+m;i++) d[i]=b[i]%10; for(int i=1;i<=n;i++) a[i]=a[i]/10; for(int i=1;i<=m;i++) b[i]=b[i]/10; kmp(); if(!tot) { cout<<"No"<<endl; return 0; } else cout<<"Yes"<<endl; pre(); for (int i=0;i<k;++i) A[i]=B[i]=Complex(); for (int i=1;i<=n;++i) A[i]=c[i]; for (int i=1;i<=m;++i) B[i]=!d[m-i+1]; fft(A,k); fft(B,k); for (int i=0;i<k;++i) A[i]=A[i]*B[i]; fft(A,k,-1); for (int i=m+1;i<=n+1;++i) ansa[i-m]=(int)(A[i].r+0.5); for (int i=0;i<k;++i) A[i]=B[i]=Complex(); for (int i=1;i<=n;++i) A[i]=!c[i]; for (int i=1;i<=m;++i) B[i]=d[m-i+1]; fft(A,k); fft(B,k); for (int i=0;i<k;++i) A[i]=A[i]*B[i]; fft(A,k,-1); for (int i=m+1;i<=n+1;++i) ansb[i-m]=(int)(A[i].r+0.5); for(int i=1;i<=n-m+1;i++) ansa[i]+=ansb[i]; int anspos,minchange=1e+9; for(int i=1;i<=tot;i++) { if(minchange>ansa[able[i]]) { minchange=ansa[able[i]]; anspos=able[i]; } } cout<<minchange<<" "<<anspos<<endl; return 0; }