码迷,mamicode.com
首页 > 其他好文 > 详细

HASH 字符串哈希 映射转化

时间:2018-06-08 00:32:46      阅读:387      评论:0      收藏:0      [点我收藏+]

标签:前缀   插入   char   基本操作   二分   origin   type   for   问题   

哈希HASH的本质思想类似于映射、离散化。

哈希,通过给不同字符赋不同的值、并且钦定一个进制K和模数,从而实现一个字符串到一个模意义下的K进制数上。

它的主要目的是判重,用于DFS、BFS判重(八数码),字符串判断相等、出现等等。

本篇总结字符串哈希以及一些应用例题。

为什要用字符串哈希?

因为取出一个字符串是O(n)的,比较一遍又是O(n)的,况且要比较两个甚至多个。这就成了n^2级别的了。

那我们比较数字怎么就不用这么麻烦呢?因为数字可以直接比较,(虽然不知道内部是怎么实现的,反正比一位一位比较肯定快)所以我们考虑把字符串映射到数字上。

就有了字符串哈希。

通过字符串哈希,只要题目支持预处理,我们可以O(n)预处理之后,O(1)进行提取,O (1)进行判重。

 

字符串哈希需要什么?

1.字符。初始坐标无所谓。

2.K进制数,通常选择131,13331,这两个质数冲突几率很小(不要问我为什么)

3.取模数,我用过 1e9+7,998244353,用2^64也可以,这里利用自然溢出,一般不会有问题。提一句,unsigned long long做减法,即使算出来应该是负数,会自动加上2^64,相当于(a+mod-b)%mod了。没有问题。

 

处理hash:

1.预处理K^len 放入k[]中储存。

2.顺便处理hash[i]=hash[i-1]*K+str[i]

 

字符串哈希的基本操作:

1.提取:a[l,r]段:hash[r]-hash[l-1]*k[r-l] 类似前缀和。

2.插入,同处理。

操作均是O(1)

 

字符串哈希支持的应用操作:

1.判断字符串是否相等。取hash段比较即可,O(1)

2.找某两个位置开始的LCP(最长公共前缀),二分位置+hash判断 O(logn) (长度够小,可用trie树,更好的支持多串LCP)

3.判断两个串字典序大小,找LCP,判断下一位大小。O(logn)

 

字符串哈希例题:

T1:POJ2758

给定一个字符串,要求维护两种操作
在字符串中插入一个字符
询问某两个位置开始的LCP
插入操作<=200,字符串长度<=5w,查询操作<=2w

分析:有人用后缀数组??不会。Splay??不会。

操作小于等于200,直接暴力重构是正解!!

注意:

1.插入字符位置可能远大于len,要向len+1取min

2.询问位置是初始位置,重构的时候,可以暴力循环记录每一个初始位置现在已经变到了第几个位置。

技术分享图片
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=80000+210;
const int mod=998244353;
const int K=13331;
ll h[N];
ll c[N];
int n,m;
int len;
int f[N];
int ne[N];
char o[N],a[N];
int main()
{
    scanf("%s",o+1);
    n=strlen(o+1);
    memcpy(a+1,o+1,sizeof o);len=n;
    //cout<<" lenn "<<len<<endl;
    scanf("%d",&m);
    for(int i=1;i<=n;i++) ne[i]=i;
    c[0]=1;
    for(int i=1;i<=n+m+1;i++) {
    c[i]=(c[i-1]*K)%mod;
    if(i<=n) h[i]=(h[i-1]*c[1]+(int)o[i])%mod;
    }
    char ch,op;
    int num,x,y;
    //cout<<"fir "<<a+1<<endl;
    while(m--){
        scanf(" %c",&op);
            
        if(op==Q){
            scanf("%d%d",&x,&y);
            x=ne[x],y=ne[y];
            //cout<<x<<" and "<<y<<endl;
            if(a[x]!=a[y]){
                printf("0\n");continue;
            }
            int ans;
            int l=0,r=min(len-x,len-y)+1;
            //cout<<" origin "<<l<<" "<<r<<endl;
            while(l<=r){
                int mid=(l+r)>>1;
                int ed1=x+mid-1;
                int ed2=y+mid-1;
                ll ha1=(h[ed1]+mod-h[x-1]*c[mid]%mod)%mod;
                ll ha2=(h[ed2]+mod-h[y-1]*c[mid]%mod)%mod;
                //cout<<mid<<" hash "<<ha1<<" "<<ha2<<endl;
                if(ha1==ha2) {
                    ans=mid,l=mid+1;
                }
                else{
                    r=mid-1;
                }
            }
            printf("%d\n",ans);
        }
        else{
            scanf(" %c%d",&ch,&num);
            if(num>len) num=len+1;
            ///add(num);
            len++;
            for(int i=len;i>=num+1;i--) a[i]=a[i-1];
            a[num]=ch;
            for(int i=num;i<=len;i++) h[i]=(h[i-1]*c[1]+(int)a[i])%mod;
            for(int i=n;i>=1;i--) {
            if(ne[i]>=num) ne[i]++;else break;}
        }
        //cout<<a+1<<endl;
    }
    return 0;
}
POJ2758

 

HASH 字符串哈希 映射转化

标签:前缀   插入   char   基本操作   二分   origin   type   for   问题   

原文地址:https://www.cnblogs.com/Miracevin/p/9153309.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!