列表

详情


NC206650. 哈希冲突

描述

哈希(hash),是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
取模是最简单的哈希方法之一,例如:设定当前模数为 5,哈希函数为 f,那么 f(7)=2,f(13) = 3 。
当两个不同的输入,经过哈希函数的运算,得到了相同的值,我们称这种现象为哈希冲突,例如:f(6)=f(11)=1。
Bob 作为学校网站的开发者,他被要求在不明文传输密码的前提下,实现用户登录,其中密码为26个小写英文字母。经过查阅资料,他找到了如下的算法:
const long long p1 = 998244353, p2 = 1000000007; void next_secret(long long &secret, long long index_number) {     secret = (secret ^ index_number) * index_number % p1 * p2 % p1; } long long string_hash(long long secret, string str) {     long long hash_value = 0;     for (int i = 0; i < str.length(); i++){         hash_value = (hash_value + secret * (str[i] - 'a' + 1)) % p1;         next_secret(secret, i + 1);     }     return hash_value; } 

这个算法的做法为,首先把小写英文字母映射成1~26的数字,使用密钥与转换后数字相乘,加到最终的哈希之中,每对被加密的字符串操作一位,都使用当前的编号,将密文按照指定算法进行更新,其中哈希模数为固定的p1。
这个算法的用法为,每次向后台发送登录请求时,随机获取一个密钥,使用密钥将用户输入的密码进行哈希,得到一个哈希值,传输时,只发送密钥和哈希值,让后台根据密钥使用相同的算法,根据数据库中保存的明文密码计算得到哈希值,若两哈希值相同,则判定为密码正确。
由于 Bob 的疏忽,在每次发送登录请求时,他并没有随机获取一个密钥,而是使用了一个常数作为密钥,这样就会大大降低这套系统的安全性。
现在,输入密钥和一个字符串,请你找到另一个与输入字符串不同的字符串(长度可与原串不同),使得他们的哈希值相等。

输入描述

第一行两个整数  以空格隔开,分别表示密钥与字符串长度,其中 

第二行输入一个字符串,仅包含小写英文字母。数据保证字符串随机生成,且至少有一个解。

输出描述

输出一行,一个字符串,表示答案。输出的字符串长度不大于 1000。若有多种可能,输出任意一种即可。

示例1

输入:

328603208 22
qohreotztnmtxupptsnuet

输出:

xgqhrvaoufpwecabqyfuoqlwjbdwd

说明:

两串的哈希值均为:224398259

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

Java 解法, 执行用时: 252ms, 内存消耗: 31672K, 提交时间: 2022-05-08 16:37:36

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

public class Main {
    private static final int M = 17;
    private static final long P1 = 998244353;
    private static final long P2 = 1000000007;
    private static final long[] SECRETS = new long[1000];
    private static final ArrayList<Long> V1 = new ArrayList<>();
    private static final ArrayList<Long> V2 = new ArrayList<>();
    private static final HashMap<Long, Integer> MAP = new HashMap<>();
    
    private static long nextSecret(long secret, int indexNumber) {
        return (secret ^ indexNumber) * indexNumber % P1 * P2 % P1;
    }
    
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] temp = reader.readLine().split(" ");
        long secret = Long.parseLong(temp[0]);
        int L = Integer.parseInt(temp[1]);
        temp[0] = reader.readLine();
        SECRETS[0] = secret;

//        生成后续秘钥
        for (int i = 1; i < 1000; i++) {
            long lastSecret = SECRETS[i - 1];
            SECRETS[i] = nextSecret(lastSecret, i);
        }
        
        for (int i = 0; i < M; i++) {
            V1.add(SECRETS[L + i]);
            V2.add(SECRETS[L + i + M]);
        }
        
        for (int i = 0; i < 1 << M; i++) {
            long sum = 0;
            for (int j = 0; j < M; j++) {
                if ((i >> j & 1) != 0) {
                    sum += V2.get(j) * 2;
                } else {
                    sum += V2.get(j);
                }
            }
            sum %= P1;
            MAP.put(sum, i);
        }
        
        for (int i = 0; i < 1 << M; i++) {
            long sum = 0;
            for (int j = 0; j < M; j++) {
                if ((i >> j & 1) != 0) {
                    sum += V1.get(j) * 2;
                } else {
                    sum += V1.get(j);
                }
            }
            sum %= P1;
            sum = (P1 - sum) % P1;
            temp[1] = temp[0];
            
//            伪造hash
            if (MAP.get(sum) != null) {
                for (int j = 0; j < M; j++) {
                    if ((i >> j & 1) != 0) {
                        temp[1] += 'b';
                    } else {
                        temp[1] += 'a';
                    }
                }
                for (int j = 0; j < M; j++) {
                    if ((MAP.get(sum) >> j & 1) != 0) {
                        temp[1] += 'b';
                    } else {
                        temp[1] += 'a';
                    }
                }
                System.out.println(temp[1]);
                break;
            }
        }
    }
}

C++ 解法, 执行用时: 77ms, 内存消耗: 7532K, 提交时间: 2022-03-11 15:44:02

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const long long p1 = 998244353, p2 = 1000000007;
void next_secret(long long &secret, long long index_number) {
    secret = (secret ^ index_number) * index_number % p1 * p2 % p1;
}
long long string_hash(long long secret, string str) {
    long long hash_value = 0;
    for (int i = 0; i < str.length(); i++){
        hash_value = (hash_value + secret * (str[i] - 'a' + 1)) % p1;
        next_secret(secret, i + 1);
    }
    return hash_value;
}
const int N = 1000;
ll val[N];
int main(){
   int x,n; cin>>x>>n;//输入密钥以及长度
    string s; cin>>s;
    val[0] = x;
    for(int i=1;i<N;i++){
        val[i] = val[i-1];
        next_secret(val[i],i);
    }
    int M = 17;
    vector<int> v1,v2;
    for(int i=0;i<M;i++){
        v1.push_back(val[n+i]);
        v2.push_back(val[n+i+M]);
    }
    map<int,int> m;
    for(int i=0;i<1<<M;i++){
        ll sum = 0;
        for(int j=0;j<M;j++){
            if(i>>j&1) sum += v2[j] * 2;
            else sum += v2[j];
        }
        sum %= p1;
        m[sum] = i;
    }
      for(int i=0;i<1<<M;i++){
        ll sum = 0;
        for(int j=0;j<M;j++){
            if(i>>j&1) sum += v1[j] * 2;
            else sum += v1[j];
        }
        sum %= p1;
        sum = (p1 - sum) % p1;
        if(m[sum]){
            for(int j=0;j<M;j++){
                if(i>>j&1) s += 'b';
                else s += 'a';
            }
            for(int j=0;j<M;j++){
                if(m[sum]>>j&1) s += 'b';
                else s += 'a';
            }
            cout<<s<<endl;
//            cout<<string_hash(x,s)<<endl;
            break;
        }
    }
}

上一题