从Hash Killer I、II、III论字符串哈希
首先,Hash Killer I、II、III是BZOJ上面三道很经典的字符串哈希破解题。当时关于II,本人还琢磨了好久,但一直不明白为啥别人AC的代码都才0.3kb左右,直到CYG神犇说可以直接随机水过去,遂恍然大悟。。。
于是,本人今天也做了下实验——假设现在有一个字符串题:输入N,接下来N行输入N个长度一样的由大写字母组成的字符串,求一共有多少种不同的字符串。此题有些类似于Hash Killer上面的原题。首先分析此题本身,两种常规办法:1.建立一棵字典树,然后可以相当方便快捷的判重,对于字符串长度均为M的数据,复杂度O(NM) 2.字符串哈希,选取一对质数pa和pb,哈希值为Sigma((ord(s1[i])-64)*pa^i) mod pb,然后通过哈希值排个序完事
接下来开始——字典树肯定能保证正确这个毫无疑问,但是更加毫无疑问的是哈希是相当容易被卡掉的(HansBug:尤其像Hash Killer II这样素数的神选取我也是醉了),但更加更加毫无疑问的是双取模哈希似乎还比较小强,于是我就此展开实验
1.写出一个数据生成器,负责随机生成N个长度为M的大写字母字符串,然后立刻用Trie树求出答案作为标准输出数据
type
point=^node;
node=record
ex:longint;
next:array['A'..'Z'] of point;
end;
var
i,j,k,l,m,n,ans:longint;
head:point;
s1,s2:ansistring;
function getpoint:point;inline;
var p:point;c1:char;
begin
new(p);p^.ex:=;
for c1:='A' to 'Z' do p^.next[c1]:=nil;
exit(p);
end;
function check(s1:ansistring):longint;inline;
var i:longint;p:point;
begin
p:=head;
for i:= to length(s1) do
begin
if p^.next[s1[i]]=nil then
p^.next[s1[i]]:=getpoint;
p:=p^.next[s1[i]];
end;
if p^.ex= then
begin
inc(ans);
p^.ex:=ans;
end;
exit(p^.ex);
end;
begin
readln(n,m);
head:=getpoint;ans:=;
RANDOMIZE;
assign(output,'hs.in');
rewrite(output);
writeln(n);
for i:= to n do
begin
s1:='';
for j:= to m do s1:=s1+chr(random()+);
writeln(s1);
check(s1);
end;
close(output);
assign(output,'hss.out');
rewrite(output);
writeln(ans);
close(output);
end.
2.接下来,开始写哈希,也不难,而且代码貌似还略短(这里面两个素数采用互换使用的模式,本程序是双取模的哈希,如果需要改成单值哈希的话直接把第50行去掉即可)
const pa=;pb=;
var
i,j,k,l,m,n:longint;
ap,bp:array[..] of int64;
a:array[..,..] of int64;
a1,a2,a3,a4:int64;
s1,s2:ansistring;
function fc(a1,a2,a3,a4:int64):longint;inline;
begin
if a1<>a3 then
if a1>a3 then exit() else exit(-)
else
if a2<>a4 then
if a2>a4 then exit() else exit(-)
else exit();
end;
procedure sort(l,r:longint);
var i,j:longint;x,y,z:int64;
begin
i:=l;j:=r;x:=a[(l+r) div ,];y:=a[(l+r) div ,];
repeat
while fc(a[i,],a[i,],x,y)=- do inc(i);
while fc(x,y,a[J,],a[J,])=- do dec(j);
if i<=j then
begin
z:=a[i,];a[i,]:=a[j,];a[j,]:=z;
z:=a[i,];a[i,]:=a[j,];a[j,]:=z;
inc(i);dec(j);
end;
until i>j;
if i<r then sort(i,r);
if l<j then sort(l,j);
end; begin
ap[]:=;bp[]:=;
for i:= to do
begin
ap[i]:=(ap[i-]*pa) mod pb;
bp[i]:=(bp[i-]*pb) mod pa;
end;
readln(n);
for i:= to n do
begin
readln(s1);
a[i,]:=;a[i,]:=;
for j:= to length(s1) do
begin
a[i,]:=(a[i,]+ap[j]*(ord(s1[j])-)) mod pb;
a[i,]:=(a[i,]+bp[j]*(ord(s1[j])-)) mod pa; //删除此行即可改为单值哈希
end;
end;
sort(,n);m:=;
a[,]:=-maxlongint;
for i:= to n do if fc(a[i-,],a[i-,],a[i,],a[i,])<> then inc(m);
writeln(m);
readln;
end.
于是开始愉快的用bat来对拍:
1.当N=100000 M=3时,很令人吃惊——单双值的哈希都问题不大(随机跑了403组数据均全部通过)
2.当N=100000 M=100是,果不其然——单值的哈希成功而华丽的实现了0%的命中率,而双值的哈希依然100%(HansBug:实测6001组数据,跑了快两小时有木有啊啊啊啊 wnjxyk:What Ghost? HansBug:我家电脑渣不解释^_^)
(HansBug:呵呵哒BZOJ3098这题我居然上来就WA了,现在看来这究竟是什么样的神人品啊)
结果已经了然,而且从bat上运行的时间来看,当N=100000 M=100时,哈希的速度比trie树看样子明显快——估计是虽然trie树可以达到O(NM),但是假如需要新建大量的点的话,那样势必相当费时,多半慢在这上面了,而哈希就是该怎么玩怎么玩——更重要的是——哈希,绝对不等同于非得开一个巨大的数组瞎搞,比如这个例子中直接排个序就完事啦。更重要的是双值哈希的稳定性还是相当不错滴!!!^_^
后记:以前我曾经一度认为hash算法一定就是必然伴随着一个硕大的数组(HansBug:搞不好还MLE有木有TT bx2k:那是必然),其实它的灵活性远远超出了我的预想,今天也算是大长了见识;还有祝愿BZOJ3099(Hash Killer III)永远不要有人AC!!!否则那就基本意味着哈希算法的终结了TT
附:对拍用的bat模板,纯手写的哦,如有雷同绝无可能么么哒
@echo off
set /a s=0
:1
set /a s=s+1
echo Test %s%
rem 此处两个数分别代表N和M,手动修改下即可
echo 10000 100|hs.exe
copy hs.in hash\hs%s%.in >nul
copy hsS.out hash\hs%s%.out >nul
echo.|time
type hash\hs%s%.in|hash.exe >hash\hs%s%.ou
echo.|time
fc hash\hs%s%.ou hash\hs%s%.out >hash\res%s%.txt
fc hash\hs%s%.ou hash\hs%s%.out
goto 1
从Hash Killer I、II、III论字符串哈希的更多相关文章
- 【BZOJ】 Hash Killer I II III
前言 这里只是一个整理... Solution Hash Killer I Hash Killer II
- Hash Killer I II
题意大概: 1.字符串hash不取模,自动溢出 构造数据卡这种hash 2.字符串hash取模1000000007,构造数据卡这种hash 题解传送门:VFleaKing http://vfleak ...
- 3097: Hash Killer I
3097: Hash Killer I Time Limit: 5 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 425 Solved: 15 ...
- 3098: Hash Killer II
3098: Hash Killer II Time Limit: 5 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 1219 Solved: ...
- BZOJ 3098: Hash Killer II(新生必做的水题)
3098: Hash Killer II Time Limit: 5 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 1555 Solved: ...
- BZOJ 3098 Hash Killer II
3098: Hash Killer II Description 这天天气不错,hzhwcmhf神犇给VFleaKing出了一道题: 给你一个长度为N的字符串S,求有多少个不同的长度为L的子串. 子串 ...
- 【BZOJ3098】 Hash Killer II
BZOJ3098 Hash Killer II Solution 这道题目好像题面里面给了提示(当然没给就有点难想了.) 曾经讲过一个叫做生日悖论的,不知道还有多少人记得 考虑相同的可能性大概是\(\ ...
- Leetcode 137. Single Number I/II/III
Given an array of integers, every element appears twice except for one. Find that single one. 本题利用XO ...
- BZOJ 3097: Hash Killer I【构造题,思维题】
3097: Hash Killer I Time Limit: 5 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 963 Solved: 36 ...
随机推荐
- jQuery css,position,offset,scrollTop,scrollLeft用法
jQuery css,position,offset,scrollTop,scrollLeft用法: <%@ page language="java" import=&quo ...
- enum 用法
public enum WeekDay { SUN(, "Sunday", "SUN"), MON(, "Monday", "MO ...
- 结合swiper使用图片懒加载
本人渣渣一枚,技术一般,记录下笔记,大神勿喷,可以留下优化建议,谢谢 最近刚刚做了个展示型的网站,使用swiper搭的框架,因为图片比较多,所 以首次加载稍微有些慢,虽然压缩过了,但是尽可能的优化吧, ...
- Bootstrap入门(十)组件4:按钮组与下拉菜单结合
Bootstrap入门(十)组件4:按钮组与下拉菜单结合 先引入本地的CSS文件和JS文件(注:1.bootstrap是需要jQuery支持的.2.需要在<body>当中添加) < ...
- java连接ms sql server各类问题解析
首先先来说下使用微软自己开发的架包进行ms sql server数据库的连接时,sql 2000与sql 2005的连接方式略有不同: 1.首先驱动不一样,sql 2000的连接驱动包有三个,分别是: ...
- MongoDB基础之一:Conetos下安装MongoDB
1.下载自己需要的版本,我这用的是mongodb-linux-x86_64-2.4.9.tgz #cd /usr/local/src # wget http://fastdl.mongodb.org/ ...
- linux-Centos6.5中nginx1.63源码安装
我自己在学习的过程中,搜索网上的教程,碰了很多壁,终于总结出自己一套易于配置和管理的安装方法 如果是用于生产环境,不用盲目追求最新版本,跟着我这个来就好了. 安装前预热: 1.创建nginx专属用户: ...
- 用手机或外部设备在同一局域网下访问虚拟主机wampsever的方法版本号是2.4.9
1,首先在虚拟服务器电脑上可以打开http://localhost/ 2,在外部设备访问时报错为:You don't have permission to access / in on this se ...
- java继承(一)
虽然说java中的面向对象的概念不多,但是具体的细节还是值得大家学习研究,java中的继承实际上就是子类拥有父类所有的内容(除私有信息外),并对其进行扩展.下面是我的笔记,主要包含以下一些内容点: 构 ...
- MySql Sql 优化技巧分享
有天发现一个带inner join的sql 执行速度虽然不是很慢(0.1-0.2),但是没有达到理想速度.两个表关联,且关联的字段都是主键,查询的字段是唯一索引. sql如下: SELECT p_it ...