长春理工大学第十四届程序设计竞赛D Capture The Flag——哈希&&打表

题目

链接

题意:给定一个字符串 $s$,求不同于 $s$ 的字符串 $t$,使得 $Hash(s) = Hash(t)$,其中 $displaystyle Hash(s) = sum_0^{len-1} x_i imes p^i$,$4 leq p,m leq 10^9$ 且$m$为质数,所有输入输出的字符串内的字符ASCII码的取值范围为 $[31, 126]$.

分析

由于最终哈希值会模 $m$,所以最多 $m$ 种哈希值,可以先建立起 $sqrt m$ 个不同哈希值的表。

具体的,利用枚举3位字符串得到 $(126-31)^3 approx 10^6$个字符串的哈希值,用map存,近似达到 $sqrt m$ 个哈希值。

先从表中选取一个字符串作为高3位,计算出低3位哈希值应该为多少,在从表中查找这个值,找到一个就退出,最多执行 $10^6$ 次。

那么单次不击中(没找到)的概率为 $displaystyle frac{m - sqrt m}{m}$,因此连续 $10^6$ 次不击中的概率为 $(frac{m - sqrt m}{m})^{1e6} = 0.00004539$,这个概率非常小,可认为总能找到答案

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
map<int, string>mp;
vector<ll>vec;
const int L = 33, R = 136;
ll p, m;
string str;

//97^3组哈希值,达到sqrt(m)
void dfs(int pos, ll v, ll pw, string s)
{
    if(pos >= 4)
    {
        mp[v] = s;  //hash(s)=v
        vec.push_back(v);
        return;
    }
    for(int i = L;i <= R;i++)  dfs(pos+1, (v + i * pw)%m, (pw * p) % m, s + (char)(i));
}

//查找哈希值x是否存在
bool find(ll x)
{
    auto it = lower_bound(vec.begin(), vec.end(), x);
    return it != vec.end() && (*it == x);
}

//求hash(s)
ll Hash(string s)
{
    ll ans = 0;
    ll pw = 1;
    for(auto ch : s)
    {
        ans += (ll)ch * pw;
        ans %= m;
        pw = pw * p % m;
    }
    return ans;
}

int main()
{
    cin >> p >> m >> str;
    ll h = Hash(str);
    //printf("hash: %lld
", h);
    
    dfs(1, 0, 1, "");

    sort(vec.begin(), vec.end());   //

    ll pw = 1;
    for(int i = 1;i <= 3;i++)  pw = pw * p % m;
    for(auto i : vec)
    {
        ll v = (h - (i * pw) % m + m) % m;
        if(find(v))
        {
            //string t=mp[v]+mp[i];
            //printf("hash2: %lld
", Hash(t));
            cout << mp[v] << mp[i] << endl;
            return 0;
        }
    }
}

参考链接:https://zhuanlan.zhihu.com/p/72702597

原文地址:https://www.cnblogs.com/lfri/p/11199865.html