• Hash


    Hash

      以前做普及试炼场的$Hash$部分是拿$Map$水过的...后来觉得还是应该学一下,发现比$Map$的用处多多啦!

      Hash有很多种,对于字符串一般使用进制$Hash$,就是把字母映射到数字上,再将整个字符串转成一个高进制数比如$233$,再对一个大质数取模,从而将字符串变成一个数字。在实际应用中,有两种需求:

      ·懒得写取模(是有多懒),可以用$unsigned long long$保存$Hash$值,相当于对$2^{64}$取模;

      ·对正确性有着极高的要求:使用两个质数,同时求两个$Hash$值,只有两个值都相等时才判断为相等,这两个质数最好去成比较大又比较接近的两个数,出现$Hash$冲撞的概率就非常低了.一本通上给出了一对很好的双$Hash$模数---孪生质数:$10^9+7,10^9+9$

      最近发现字符串类的题目还是用$char$数组比较方便,不容易出一些奇奇怪怪的错误,下面是使用字符数组做Hash题目的一些常用操作:

      头文件部分: 

      1 # include <cstring>
      2 # include <string>
      3 # define ULL unsigned long long

      对于思想永远僵化在$Pascal$的人群,其实字符数组的下标也是可以从$1$开始用的:

      1 scanf("%s",s+1);
      2 l=strlen(s+1); //求长度 

      计算进制的各个幂,方便之后截取子串以及滚动求字符串的前缀$Hash$值:

      1 po[0]=1;
      2 for (R i=1;i<=maxn;++i)
      3     po[i]=po[i-1]*base;
      4 for (R i=1;i<=l;++i)
      5     hash[i]=hash[i-1]*base+s[i]-'A'+1;

      截取$[l,r]$子串的$Hash$值: 

      1 a=hash[r]-hash[l]*po[r-l+1];

      如果感到难以理解,就把进制换成10来想一想,提交的时候不要忘记改回233就可以了.

      Hash当然还有许多种,比如$Xor-Hash$(个人觉得非常不靠谱),$mod-hash$(虽然也觉得不靠谱,但是一般配合$Hash$表使用,就没有这样的顾虑了),一本通上还介绍了奇妙的将数字乘上一个无理数再取整的方法...

      最终发现还是进制$Hash$最好用。根据生日悖论,出现冲撞的概率非常小,出现冲撞的步长是$sqrt{mod}$,但是模数非常大以至于几乎可以忽略这个概率.说到生日悖论,暑假$shallwe$来讲课,发现机房$50$多个人竟然没有生日在同一天的23333

      刚刚浏览器挂掉了,然后我已经写完的两道题也跟着消失了,真伤心。

      再来一次:

      POJ 2752:http://poj.org/problem?id=2752

      题意概述:对于给定的串,询问它的哪些前缀同时也是后缀。

      $Hash$模板题。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cstring>
     4 # include <string>
     5 # define R register int
     6 # define ULL unsigned long long
     7 
     8 using namespace std;
     9 
    10 const int base=233;
    11 const int maxn=400005;
    12 char c[maxn];
    13 int l;
    14 ULL hash[maxn];
    15 ULL po[maxn];
    16 
    17 int main()
    18 {
    19     po[0]=1;
    20     for (R i=1;i<=maxn;++i)
    21         po[i]=po[i-1]*base;
    22     while (~scanf("%s",c+1))
    23     {
    24         l=strlen(c+1);
    25            for (R i=1;i<=l;++i)
    26             hash[i]=hash[i-1]*base+c[i]-'a'+1;
    27         for (R i=1;i<=l;++i)
    28             if(hash[i]==hash[l]-hash[l-i]*po[i]) printf("%d ",i);
    29         printf("
    ");
    30     }
    31     return 0;
    32 }
    POJ 2752

     

      POJ 3461:http://poj.org/problem?id=3461

      题意概述:给定两个串,求$A$串在$B$串的哪些位置出现过.

      枚举在$B$中的起点,截取子串进行$Hash$判断.

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cstring>
     4 # include <string>
     5 # define R register int
     6 # define ULL unsigned long long
     7 
     8 using namespace std;
     9 
    10 const int base=233;
    11 const int maxn=1000005;
    12 int l1,l2,ans,T;
    13 char w[10005],t[maxn];
    14 ULL po[maxn],hash[maxn],goa;
    15 
    16 int main()
    17 {
    18     po[0]=1;
    19     for (R i=1;i<=maxn;++i)
    20         po[i]=po[i-1]*base;
    21     scanf("%d",&T);
    22     while (T--)
    23     {
    24         ans=0;
    25         scanf("%s",w+1);
    26         scanf("%s",t+1);
    27         goa=0;
    28         l1=strlen(w+1);
    29         l2=strlen(t+1);
    30         for (R i=1;i<=l2;++i)
    31             hash[i]=hash[i-1]*base+t[i]-'A'+1;
    32         for (R i=1;i<=l1;++i)
    33             goa=goa*base+w[i]-'A'+1;
    34         for (R i=1;i<=l2-l1+1;++i)
    35             if(hash[i+l1-1]-hash[i-1]*po[l1]==goa) ans++;    
    36         printf("%d
    ",ans);
    37     }    
    38     return 0;
    39 }
    POJ 3461

     

      火星人:https://www.lydsy.com/JudgeOnline/problem.php?id=1014

      题意概述:带修改带插入的LCP问题。$L<=10^5,M<=1.5 imes 10^5$

      SA好像并不能资瓷修改和插入,所以只好想一个更暴力的办法:二分+Hash;如果只有修改,可以用线段树做,但是如果要插入就只能用平衡树了。如果用平衡树维护前缀哈希值,可能就崩溃了,因为平衡树并不能O(1)地求出前驱后继,所以需要另辟蹊径...维护区间哈希值!这个可以通过左右儿子的区间哈希值和子树大小 $O(1)$ 维护,从这里入手就可以解决了。说起来容易做起来难,调了一晚上才调出来,比较开心的是在洛谷上竟然1A了。bz上T掉了,所以进行了一番魔改,后来发现有的地方少Splay几次就玄学的快起来了。

      
      1 # include <cstdio>
      2 # include <iostream>
      3 # include <cstring>
      4 # include <string>
      5 # define R register int
      6 # define ULL unsigned long long
      7 
      8 using namespace std;
      9 
     10 const int maxn=100005;
     11 const int base=233;
     12 char s[maxn],opt[10],cc[4];
     13 ULL po[maxn<<2];
     14 int m,x,y,n,vis[maxn],cnt,l,r,rt,c;
     15 struct node
     16 {
     17     int siz,f,ch[2];
     18     int x;
     19     ULL v;
     20 }t[maxn];
     21 
     22 void update (int x)
     23 {
     24     int l=t[x].ch[0],r=t[x].ch[1];
     25     if(t[l].v==0&&t[r].v==0) 
     26         t[x].v=t[x].x;
     27     else 
     28         if(t[r].v==0) 
     29             t[x].v=t[l].v*base+t[x].x;
     30     else
     31         t[x].v=(t[l].v*base+t[x].x)*po[ t[r].siz ]+t[r].v;
     32     t[x].siz=t[l].siz+t[r].siz+1;
     33 }
     34 
     35 int D (int x) { return t[ t[x].f ].ch[1]==x; }
     36 
     37 void rotate (int x)
     38 {
     39     int f=t[x].f,ff=t[f].f;
     40     int dx=D(x),df=D(f);
     41     int k=t[x].ch[dx^1]; 
     42     t[k].f=f;
     43     t[f].ch[dx]=k;
     44     t[f].f=x;
     45     t[x].ch[dx^1]=f;
     46     t[x].f=ff;
     47     t[ff].ch[df]=x;
     48     update(f);
     49     update(x);
     50 }
     51 
     52 void Splay (int x,int g)
     53 {
     54     while(t[x].f!=g)
     55     {
     56         if(t[ t[x].f ].f==g) rotate(x);
     57         else if(D(x)==D(t[x].f)) rotate(t[x].f),rotate(x);
     58         else rotate(x),rotate(x);
     59     }
     60     update(x);
     61     if(!g) rt=x;
     62 }
     63 
     64 void ins (int l,int r,int v)
     65 {
     66     Splay(l,0),Splay(r,l);
     67     t[r].ch[0]=++cnt;
     68     t[cnt].siz=1;
     69     t[cnt].x=t[cnt].v=v;
     70     t[cnt].f=r;
     71     update(r),update(l);
     72     Splay(cnt,0);
     73 }
     74 
     75 int find (int k)
     76 {
     77     int x=rt;
     78     while (1)
     79     {
     80         int s=t[ t[x].ch[0] ].siz;
     81         if (s>=k) x=t[x].ch[0];
     82         else if(s+1==k) return x;
     83         else x=t[x].ch[1],k-=s+1; 
     84     }
     85 }
     86 
     87 ULL ask (int x)
     88 {
     89     if(x==0) return 0;
     90     int l=find(1),r=find(x+2);
     91     Splay(l,0);
     92     Splay(r,l);
     93     ULL ans=t[ t[r].ch[0] ].v;
     94 //    x=find(x);
     95 //    Splay(x,0); 这两句不加的话好像复杂度不是很对,但是加上后就跑的特慢,所以去掉了 
     96     return ans;
     97 }
     98 
     99 bool check (int a,int b,int k,ULL A,ULL B)
    100 {
    101     ULL x,y;
    102     x=ask(a+k-1)-A*po[k];
    103     y=ask(b+k-1)-B*po[k];
    104     return (x==y);
    105 }
    106 
    107 int solve (int x,int y)
    108 {
    109     if(x>y) swap(x,y);
    110     int l=1,r=n-y+1,mid,ans=0;
    111     ULL A=ask(x-1),B=ask(y-1);
    112     while(l<=r)
    113     {
    114         mid=(l+r)>>1;
    115         if(check(x,y,mid,A,B)) ans=mid,l=mid+1;
    116         else r=mid-1;
    117     }
    118     return ans;
    119 }
    120 
    121 int read ()
    122 {
    123     R x=0;
    124     char c=getchar();
    125     while (!isdigit(c)) c=getchar();
    126     while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    127     return x;
    128 }
    129 
    130 int main()
    131 {
    132     scanf("%s",s+1);
    133     n=strlen(s+1);
    134     scanf("%d",&m);
    135     po[0]=1;
    136     for (R i=1;i<=n+m;++i) po[i]=po[i-1]*base;
    137     cnt=2;
    138     rt=1;
    139     t[2].f=1;
    140     t[1].ch[1]=2;
    141     for (R i=1;i<=n;++i)
    142     {
    143         l=find(i),r=find(i+1);
    144         x=s[i]-'a'+1;
    145         ins(l,r,x);
    146     }
    147     for (R i=1;i<=m;++i)
    148     {
    149         scanf("%s",opt+1);
    150         if(opt[1]=='Q')
    151         {
    152             x=read(),y=read();
    153             printf("%d
    ",solve(x,y));
    154         }
    155         else if(opt[1]=='R')
    156         {
    157             scanf("%d",&x);
    158             scanf("%s",cc+1);
    159             c=cc[1]-'a'+1;
    160             l=x,r=x+2;
    161             l=find(l),r=find(r);
    162             Splay(l,0);
    163             Splay(r,l);
    164             x=t[r].ch[0];
    165             t[x].v=t[x].x=c;
    166             update(r);
    167             update(l);
    168             Splay(x,0);
    169         }
    170         else if(opt[1]=='I')
    171         {
    172             scanf("%d",&x);
    173             scanf("%s",cc+1);
    174             c=cc[1]-'a'+1;
    175             l=find(x+1);
    176             r=find(x+2);
    177             Splay(l,0);
    178             Splay(r,l);
    179             x=t[r].ch[0]=++cnt;
    180             t[x].v=t[x].x=c;
    181             t[x].siz=1;
    182             t[x].f=r;
    183             update(r);
    184             update(l);
    185             Splay(x,0);
    186             n++;
    187         }
    188     }
    189     return 0;
    190 }
    火星人

     

    ---shzr

  • 相关阅读:
    排序算法之希尔排序
    排序算法之直接插入排序
    PL/SQL之异常
    PL/SQL之包
    PL/SQL之存储过程和函数
    Oracle左连接、右连接、全外连接以及(+)号用法
    PL/SQL之存储过程和触发器实例
    PL/SQL之游标的使用
    Tag Tree
    目录:JAVA
  • 原文地址:https://www.cnblogs.com/shzr/p/9657484.html
Copyright © 2020-2023  润新知