BJ模拟:图片加密(KMP+FFT)

Description

      CJB天天要跟妹子聊天,可是他对微信的加密算法表示担心:“微信这种加密算法,早就过时了,我发明的加密算法早已风靡全球,安全性天下第一!”

      CJB是这样加密的:设CJB想加密的信息有  m  个字节。首先,从网上抓来一张  n(nm)  个字节的图片,分析里面的每个字节(byte)。每个字节有8位(bit)二进制数字。他想替换掉某些字节最低位的二进制数字,使得这张图片中,连续  m  个字节恰为他想加密的信息。这样,图片看起来没什么区别,却包含了意味深长的信息。

      很显然,不是所有的图片都能让CJB加密他那  m  个字节的信息。他想请你帮忙写个程序判断这张图片是否能加密指定的信息。如果可以加密,则CJB要求改变最少的字节数。如果仍有多种解,他希望信息在图片中的位置越前越好。

Input

      第一行包含两个整数  n,m(1n,m250000)  ,表示图片的大小和信息的大小。

      第二行表示图片的内容。

      第三行表示信息的内容。

      所有内容都以二进制字节的形式给出。每个字节有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,m500

      对于40%的数据,  n,m5000

      对于70%的数据,  n,m105

      对于所有数据,  1n,m2.5×105

题解:

对于每个二进制除最后一位的数都必须与原串匹配,可以用KMP做

对于每个二进制最后一位需计算与原串最后一位不同的个数,可用FFT做:

将原串反向建,再FFT求卷积a*b,并计算a^2、b^2前缀和;

之后用a^2+b^2-2*ab=(a-b)^2即可求出不同位个数 复杂度为O(nlogn).


代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int Maxn=2e5+5e4+50;
const int Maxm=Maxn*8;
const int INF=0x3f3f3f3f;
const double PI2=acos(-1.0)*2;
inline int read()
{
    char ch=getchar();int i=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
    return i*f;
}
struct complex
{
    double r,i;
    complex(double a=0,double b=0):r(a),i(b){}
    friend inline complex operator +(const complex &a,const complex &b){return complex(a.r+b.r,a.i+b.i);}
    friend inline complex operator -(const complex &a,const complex &b){return complex(a.r-b.r,a.i-b.i);}
    friend inline complex operator *(const complex &a,const complex &b){return complex(a.r*b.r-a.i*b.i,a.i*b.r+a.r*b.i);}
    inline complex conj(){return complex(r,-i);}
}A[Maxm],B[Maxm],C[Maxm];
struct FFT 
{
    int k,pos[Maxm];
    inline void Init(int len)
    {
        for(k=1;k<=len;k<<=1);
        k<<=1;
        for(int i=1;i<=k;i++)
        pos[i]=(i&1)?((pos[i>>1]>>1)^(k>>1)):(pos[i>>1]>>1);
    }
    inline void dft(complex *a,int dft)
    {
        for(int i=1;i<k;i++)
        if(i<pos[i])swap(a[i],a[pos[i]]);
        for(int m1=1,m2;m1<k;m1<<=1)
        {
            m2=m1<<1;
            double temp=PI2/m2*(double)dft;
            complex wn(cos(temp),sin(temp));
            for(int i=0;i<k;i+=m2)
            {
                complex w(1,0);
                for(int j=0;j<m1;j++)
                {
                    complex &A=a[i+j],&B=a[i+j+m1],t=B*w;
                    B=A-t;A=A+t;w=w*wn;
                }
            }
        }
    }
    inline void muitiply(complex *a,complex *b,complex *c)
    {
        dft(a,1),dft(b,1);
        for(int i=0;i<k;i++)c[i]=a[i]*b[i];
        dft(c,-1);
        for(int i=0;i<k;i++)c[i].r/=(double)k;
    }
}fft;
int n,m;
int a1[Maxn],a2[Maxn];
int b1[Maxn],b2[Maxn];
int nxt[Maxn],pos[Maxn],cnt,ans,maxn=INF;
inline void getnxt()
{
    for(int i=2,j=0;i<=m;i++)
    {
        while(j&&b1[j+1]!=b1[i])j=nxt[j];
        if(b1[j+1]==b1[i])j++;
        nxt[i]=j;
    }
}
inline void kmp()
{
    getnxt();
    for(int i=1,j=0;i<=n;i++)
    {
        while(j&&a1[i]!=b1[j+1])j=nxt[j];
        if(a1[i]==b1[j+1])j++;
        if(j==m)
        {
            pos[++cnt]=i-j+1;
            j=nxt[j];
        }
    }
}
inline void calc()
{
    fft.Init(n+m);
    for(int i=1;i<=n;i++)A[i-1].r=a2[i];
    for(int j=1;j<=m;j++)B[m-j].r=b2[j];
    for(int i=1;i<=n;i++)a2[i]+=a2[i-1];
    for(int i=1;i<=m;i++)b2[i]+=b2[i-1];
    fft.muitiply(A,B,C);
    for(int i=1;i<=cnt;i++)
    {
        int t=a2[pos[i]+m-1]-a2[pos[i]-1]+b2[m]-2*floor(C[pos[i]+m-2].r+0.5);
        if(t<maxn)
        {
            ans=i,maxn=t;
        }
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        a1[i]=read();
        a2[i]=a1[i]%10;
        a1[i]/=10;
    }
    for(int i=1;i<=m;i++)
    {
        b1[i]=read();
        b2[i]=b1[i]%10;
        b1[i]/=10;
    }
    kmp();
    if(!cnt)puts("No");
    else
    {
        puts("Yes");
        calc();
        printf("%d %d",maxn,pos[ans]);
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值