[题解][2021-2022年度国际大学生程序设计竞赛第10届陕西省程序设计竞赛] Hash

题目描述

给定字符串T,要求求字符串S,满足以下条件:

  • S是T的前缀
  • S和T运行某段代码的哈希值相同(代码见下)
  • T只包含小写字母
  • S和T的长度差不超过50

哈希代码:

//Language C++14
long long mod=5999993;
long long gethas(string s)
{
    long long ret=0;
    for (char c:s)
    ret=(ret*29+(c-'a'+1))%mod;
    return ret;
}

题解
先分析这段哈希代码。取的模数mod=5999993,位权值为29(相当于29进制)。
那么可以得到29^6约为mod*100,远大于mod。
设S的哈希值为X,将X左移六位的哈希值为Y。可以枚举一个i,使i倍mod+X=H+Y。
其中H是可以用六位小写字母表示的哈希值。
由于a-z可以表示1-26的所有数,对于29进制,只要每一位的数满足>0且<27即可用小写字母表示。
每次查询能够找到的概率为(26/29)^6 ,约等于0.51。玄学复杂度,但可以跑过。

代码

#include<bits/stdc++.h>
#define  int  long long
using namespace std;
const int mod=5999993;
int q_pow(int a,int x){
	int ans=1,base=a;
	while(x){
		if(x&1)(ans*=base)%=mod;
		(base*=base)%=mod;
		x>>=1;
	}
	return ans;
}
bool check(int x){
	int ans[10];
	int p=0;
	while(x){
		ans[++p]=x%29;
		x/=29;
		if(ans[p]<=0||ans[p]>26)return false;
	}
	if(p!=6)return false;
	for(int i=p;i>=1;i--)cout<<(char)('a'+ans[i]-1);
	cout<<"\n";
	return true;
}
signed main(){
	int t;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		cout<<s;
		int x=0,f=1;
		for(int i=s.length()-1;i>=0;i--)(x+=((int)(s[i]-'a'+1)*f))%=mod,(f*=29)%=mod;
		int y=x;
		int flag=0;
		while(1){
			(y=q_pow(29,6)*y)%=mod;
			for(int i=0;i<=100;i++){
				int delta=x+mod*i-y;
				if(check(delta)){
					flag=1;
					break;
				}
			}
			if(flag)break;
		} 
	}
} 
posted @ 2024-04-15 20:32  ZWZWW  阅读(33)  评论(0编辑  收藏  举报