URAL 1996 Cipher Message 3 FFT + KMP

来源:互联网 发布:重庆优化公司哪家好 编辑:程序博客网 时间:2024/05/20 23:40

题目大意:

就是现在给出一幅画的存储代码为一组n个01串, 每个串的长度都是8, 现在有一串需要加密进去的01串, 长度为m个(n, m <= 250000)

现在可以将n个01串中的最后一个数字进行更改, 前7个01不能更改, 问是否能将这n个01串的末尾进行更改使得这个长度为m的加密消息出现在这个改动后的01串中


首先注意这题只有当前7位相同时才有可能改成相同, 那么可以先只看每个01串的前7位进行KMP匹配, 在O(n + m)的复杂度内找到所有可行的位置, 对于每个匹配位置需要进行的改动数量, 其实就是两个串之间的Hamming距离, 这个可以参见UVALive 4671的解法, 直接用FFT就可以在O(nlogn)的时间内得到, 那么这个问题也就解决了


代码如下:

Result  :  Accepted     Memory  :  29940 KB     Time  :  780 ms

/* * Author: Gatevin * Created Time:  2015/7/16 14:50:22 * File Name: URAL1996.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;const double PI = acos(-1.0);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[1 << 19];void FFT(Complex *a, int len, int DFT){    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));        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;}Complex N[1 << 19], M[1 << 19];int inN[250010], inM[250010];int next[250010];vector <int> pos;//表示匹配成功的开头位置int dif[250010];int n, m;void KMP(){    memset(next, 0, sizeof(next));    for(int i = 1; i < m; i++)    {        int j = i;        while(j > 0)        {            j = next[j];            if((inM[i] >> 1) == (inM[j] >> 1))            {                next[i + 1] = j + 1;                break;            }        }    }    pos.clear();    for(int i = 0, j = 0; i < n; i++)    {        if((inM[j] >> 1) == (inN[i] >> 1))        {            j++;        }        else        {            while(j > 0)            {                j = next[j];                if((inM[j] >> 1) == (inN[i] >> 1))                {                    j++;                    break;                }            }        }        if(j == m) pos.push_back(i - m + 1);    }}char tmp[10];int main(){    while(~scanf("%d %d", &n, &m))    {        for(int i = 0; i < n; i++)        {            scanf("%s", tmp);            inN[i] = 0;            for(int k = 0; k < 8; k++)            {                inN[i] <<= 1;                if(tmp[k] == '1') inN[i] |= 1;            }        }        for(int i = 0; i < m; i++)        {            scanf("%s", tmp);            inM[i] = 0;            for(int k = 0; k < 8; k++)            {                inM[i] <<= 1;                if(tmp[k] == '1') inM[i] |= 1;            }        }        if(n < m)        {            puts("No");            continue;        }        KMP();        if(pos.empty())        {            puts("No");            continue;        }        //接下来FFT处理一下        int len = 1;        while(len <= n) len <<= 1;        len <<= 1;                for(int i = 0; i < n - m + 1; i++) dif[i] = m;                //先算出都是1的匹配位置        for(int i = 0; i < n; i++)            N[i] = Complex(inN[i] & 1, 0);        for(int i = n; i < len; i++)            N[i] = Complex(0, 0);        for(int i = 0; i < m; i++)//将M序列反向过来求两个串的Hamming距离(不同字符数)            M[i] = Complex(inM[m - i - 1] & 1, 0);        for(int i = m; i < len; i++)            M[i] = Complex(0, 0);        FFT(N, len, 1); FFT(M, len, 1);        for(int i = 0; i < len; i++)            N[i] = N[i] * M[i];        FFT(N, len, -1);        for(int i = 0; i < n - m + 1; i++) dif[i] -= (int)(N[i + m - 1].real + 0.5);                //接下来算出都是0的位置        for(int i = 0; i < n; i++)            N[i] = Complex(1 - (inN[i] & 1), 0);        for(int i = n; i < len; i++)            N[i] = Complex(0, 0);        for(int i = 0; i < m; i++)            M[i] = Complex(1 - (inM[m - i - 1] & 1), 0);        for(int i = m; i < len; i++)            M[i] = Complex(0, 0);        FFT(N, len, 1); FFT(M, len, 1);        for(int i = 0; i < len; i++)            N[i] = N[i] * M[i];        FFT(N, len, -1);        for(int i = 0; i < n - m + 1; i++) dif[i] -= (int)(N[i + m - 1].real + 0.5);                int minDif = 1e9 + 7, ansPos = -1;        puts("Yes");        for(int i = 0, sz = pos.size(); i < sz; i++)            if(minDif > dif[pos[i]])                minDif = dif[pos[i]], ansPos = pos[i];        printf("%d %d\n", minDif, ansPos + 1);    }    return 0;}


0 0