URAL-1996 (KMP+FFT)

原题题面

Leia: I have placed information vital to the survival of the rebellion into the memory systems of this R2 unit. My father will know how to retrieve it. You must see this droid safely delivered to him on Alderaan.
Emperor Palpatine has been ruling the Empire for 25 years and Darth Vader has been the head of the Empire Armed Forces. However, the Rebel movement is strong like it never used to be. One of the rebel leaders, Princess Leia from Alderaan, managed to get hold of secret blueprints of the Death Star, the imperial war station.
The Princess was going to deliver the station plan to the secret base for further analysis and searching for vulnerable spots. But her ship was attacked by the space destroyer “Devastator” headed by Darth Vader. At the last moment Princess Leia managed to send her findings to one of the closest planet called Tatooine with her droid R2-D2. Quite conveniently, an old friend of her father Obi-Wan Kenobi lives on that planet.
R2-D2 realizes the importance of his mission. He is going to encrypt the information so that the wrong people won’t get it.
The memory of R2-D2 has many files with images. First he wanted to use a well-known encrypting algorithm. The point of the method is to replace the least significant bits of the image with the encrypted message bits. The difference is practically unnoticeable on the picture, so one won’t suspect that it contains a hidden message.
But then R2-D2 decided that this method is quite well-known and the information won’t be protected enough. He decided to change the least significant bits of the image so that the secret information was a continuous sequence of the bytes of the image file. Help the droid determine if it is possible. And if it is, find the minimum number of bits to alter.

输入格式

The first line of the input contains integers n and m (1 ≤ n, m ≤ 250 000) — the sizes of the image file and of the file with the secret information in bytes. On the second line the content of the file with an image is given and the third line contains the secret information. The files are given as a sequence of space-separated bytes. Each byte is written as a sequence of eight bits in the order from the most to the least significant bit.

输出格式

Print “No”, if it is impossible to encrypt information in this image. Otherwise, print in the first line “Yes”, and in the second line — the number of bits to alter and the number of the byte in the file with the image, starting from which the secret information will be recorded. If there are multiple possible variants, print the one where the secret information is written closer to the beginning of the image file.

输入样例1

3 2
11110001 11110001 11110000
11110000 11110000

输出样例1

Yes
1 2

输入样例2

3 1
11110000 11110001 11110000
11110000

输出样例2

Yes
0 1

题面分析

不知道是我语文太差还是怎么的,看懂题面就花了不少时间…
简单的说就是:有A(包含n个8位01串)和B(包含m个8位01串),问是否可以通过改变n个串中每个串的最后一位,使得B是A的子串,如果是,请给出改变的位数与第一个被改变的位置(如有多方案取被改变位置最小的)
既然每个01串一共是8位,那首先我们可以对前面7位做一次KMP,找到哪些位置是合理的(可以把前七位看成一个十进制数字然后对两个int数组做kmp)。
那如何计算需要改变的位置个数呢?
首先显而易见,需要改变的情况就是a[i]=1,b[i]=0或a[i]=0,b[i]=1。
以第一种情况为例,我们记A中的每个最后一位是否为1是 a [ i ] a[i] a[i],B中的每个最后一位是否为0是 b [ i ] b[i] b[i]
那么对于 A [ k , k + m − 1 ] A[k,k+m-1] A[k,k+m1] B [ 0 , m ] B[0,m] B[0,m]需要改变的方案数就是
∑ i = 0 m a [ i + k ] b [ i ] \sum_{i=0}^{m}a[i+k]b[i] i=0ma[i+k]b[i]
(只有1*1的时候才有贡献,即a[i]=1,b[i]=0)
到了这一步就是经典结论了,我们翻转 b b b,使得 b [ i ] = b [ m − i − 1 ] b[i]=b[m-i-1] b[i]=b[mi1],于是得到原式等价于
∑ i + j = m + k − 1 a [ i ] b [ j ] \sum_{i+j=m+k-1}a[i]b[j] i+j=m+k1a[i]b[j]
用FFT套一下即可。
于是我们现在得到了需要改变的位置个数,只需要在KMP里完成匹配时把对应位置的方案数输出即可。

AC代码(859ms)

#include<bits/stdc++.h>
using namespace std;
const int maxn=6e5;
#define cp complex<double>
#define ll long long
#define inf 0x3f3f3f3f
cp a[maxn+10], b[maxn+10];
const double pi=acos(-1.0);
ll rev[maxn+10];
int val1[maxn+10];
int val2[maxn+10];
int bit1[maxn+10];
int bit2[maxn+10];
int result[maxn+10];
int getBit(int n)
{
    int len=1;
    while(len<(n<<1)) len<<=1;
    for(int i=0; i<len; i++) rev[i]=(rev[i>>1]>>1)|(i&1 ? len>>1 : 0);
    return len;
}
void FFT(cp *a, int n, int flag)
//inv是1时是系数转点值,-1是点值转系数
{
    for(int i=0; i<n; ++i)
    {
        if (i<rev[i]) swap(a[i], a[rev[i]]);
    }
    for(int mid=1; mid<n; mid*=2)
    {
        cp w(cos(pi*1.0/mid), flag*sin(pi*1.0/mid));//单位根
        for(int i=0; i<n; i+=mid*2)
        {
            cp omega(1, 0);
            for(int j=0; j<mid; j++, omega*=w)
            {
                cp x=a[i+j];
                cp y=omega*a[i+j+mid];
                a[i+j]=x+y;
                a[i+j+mid]=x-y;
            }
        }
    }
}
int index=maxn+10, len=inf;
int Next[maxn+10];
void GetNext(int p[], int lenp)
{
    Next[0]=-1;
    int k=-1;
    int j=0;
    while(j<lenp)
    {
        //p[k]为前缀,p[j]为后缀
        if (k==-1 || p[j]==p[k])
        {
            k++;
            j++;
            Next[j]=k;//如果匹配,说明最大相同前后缀
        }
        else
        {
            k=Next[k];
        }
    }
}
void PrintNext(int lenp)
{
    for(int i=0; i<=lenp; i++)
    {
        printf("i=%d next=%d\n", i, Next[i]);
    }
    printf("\n");
}
void GetKMP(int s[], int p[], int lens, int lenp)
{
    int i=0, j=0;
    while(i<lens && j<lenp)
    {
        if (j==-1 || s[i]==p[j])//匹配成功
        {
            i++;
            j++;
        }
        else
        {
            j=Next[j];
        }
        if (j==lenp)
        {
            if (len>result[i-1])
            {
                len=result[i-1];
                index=i-j+1;
            }
            j=Next[j];
        }
    }
    return;
}
void KMP(int s[], int p[], int lens, int lenp)
{
    GetNext(p, lenp);
    return GetKMP(s, p, lens, lenp);
}
void f(int v1, int v2, int n, int m, int limit)
{
    for(int i=0; i<limit; ++i)
    {
        a[i].real(i<n ? bit1[i]==v1 ? 1 : 0 : 0);
        a[i].imag(0);
    }
    for(int i=0; i<limit; ++i)
    {
        b[i].real(i<m ? bit2[m-i-1]==v2 ? 1 : 0 : 0);
        b[i].imag(0);
    }
    FFT(a, limit, 1);
    FFT(b, limit, 1);
    for(int i=0; i<limit; ++i)
    {
        a[i]*=b[i];
    }
    FFT(a, limit, -1);
    for(int i=0; i<limit; ++i)
    {
        result[i]+=(int) (a[i].real()/limit+0.5);
    }
}
void solve()
{
    int n, m, limit;
    scanf("%d%d", &n, &m);
    limit=getBit(n);
    for(int i=0; i<n; ++i)
    {
        int x;
        scanf("%d", &x);
        bit1[i]=x&1;
        val1[i]=x/10;
    }
    for(int i=0; i<m; ++i)
    {
        int x;
        scanf("%d", &x);
        bit2[i]=x&1;
        val2[i]=x/10;
    }
    if (m>n)
    {
        printf("No\n");
        return;
    }
    f(1, 0, n, m, limit);
    f(0, 1, n, m, limit);
//    for(int i=0; i<limit; ++i)
//    {
//        printf("%d ", result[i]);
//    }
//    printf("\n");
    KMP(val1, val2, n, m);
    if (len==inf)
    {
        printf("No\n");
    }
    else
    {
        printf("Yes\n%d %d\n", len, index);
    }
}
signed main()
{
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long long test_index_for_debug=1;
    char acm_local_for_debug;
    while(cin>>acm_local_for_debug)
    {
        cin.putback(acm_local_for_debug);
        if (test_index_for_debug>100)
        {
            throw runtime_error("Check the stdin!!!");
        }
        auto start_clock_for_debug=clock();
        solve();
        auto end_clock_for_debug=clock();
        cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
        cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
            <<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
        cout<<"--------------------------------------------------"<<endl;
    }
#else
    solve();
#endif
    return 0;
}

后记

为什么FFT可以和搞KMP…
DrGilbert 2020.10.9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值