哈希Hash在字符串中的應用_C++


  本文含有原創題,涉及版權利益問題,嚴禁轉載,違者追究法律責任

 

  哈希大家都會用撒,字符串顯然都會寫撒,那么哈希離散化字符串不就懂了?!(XXX的神邏輯,其實原文是:樹都曉得吧,數組顯然都會開呀,那么恭喜你學會了樹狀數組!)

  例如我們給出 n 個長度為 m 的字符串,然后給你一個長度為 m 的字符串 s ,求 s 是否在之前的 n 個串中出現過

  暴力掃 n 個串,然后一位位去對,看是否相等,時間復雜度O(nm),它非常得辣雞

  當然我們可以建一顆 trie 樹做,但是建樹的復雜度也是O(nm)的,對於只詢問一次還不如打暴力

  一陣思考后,我們選用非常優秀的哈希(以下簡稱Hash)

  (機制的人請忽略這篇文章)

  平常我們用 Hash 是對數字進行處理,一般把它取模之后離散化到各個數組

  但是對於字符串呢?難道把它所有位加起來取模?顯然是不行的,如 ab 和 ba 這兩種情況是相同的,那么小節點掛的鏈會很多

  於是乎,我們想,對於字符串只有256種,而題目中給的一般只會包含字母,甚至是只有小寫字母,瞬間壓縮到26種

  那么我們可以把它前面 k 位取出來,如取4位出來是 axoc,把它裝換成對應的數字是 0 23 14 2 (a 看做 0,b 看做 1 …… z 看做 25)

  這不就是一個二十六進制數!

  把前 k 位取出來,第 i 位即為這個26進制數的第 i 位,再把它轉換為10進制,如 axoc 為 45214,這樣我們就得到了 Hash 的 Hash 函數了

  字符串判重就容易了, Hash 具體實現過程就不用講了吧

  對於 26 進制的數,int 范圍下只能取到第 6 位,long long范圍下可以取到第 13 位

    

  下面給道例題

  題意很簡單,給你一個串,求他有多少個不同的子串,滿足前綴為A,后綴為B。 需要注意的是,串中所有的字母都是小寫字母。

  三個串長度均小於等於 2000,時間1s,空間128MB

  樣例輸入:abababab

       a

       b

  樣例輸出:4

  直接暴力枚舉然后 Hash 判重,我們這里截取前3位做 Hash 函數

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<iostream>
  6 #include<algorithm>
  7 using namespace std;
  8 
  9 const int N=11000,loc=3,power[8]={0,1,26,676,17576,456976,11881376,308915776},mo=17576;
 10 struct Hash
 11 {
 12     int next;
 13     char s[N];
 14 }
 15     hs[N*2];
 16 char a[N],b[N],s[N];
 17 int ls,ans,next[N],f[N],top,first[mo],tg,g[mo];
 18 bool fa[N],fb[N];
 19 void init(char c[N],int len)
 20 {
 21     int i,j=0;
 22     for (i=1;i<len;i++)
 23     {
 24         while (j&&c[i]!=c[j]) j=next[j];
 25         if (c[i]==c[j]) j++;
 26         next[i+1]=j;
 27     }
 28 }
 29 void kmp(char c[N],int len)
 30 {
 31     int i,j=0;
 32     top=0;
 33     for (i=0;i<=len;i++) next[i]=0;
 34     init(c,len);
 35     for (i=0;i<ls;i++)
 36     {
 37         while (j&&s[i]!=c[j]) j=next[j];
 38         if (s[i]==c[j]) j++;
 39         if (j==len)
 40         {
 41             f[++top]=i-j+1;
 42             j=next[j];
 43         }
 44     }
 45 }
 46 void hash(char c[N],int len)
 47 {
 48     int i,x=0,t=min(len,loc);
 49     for (i=1;i<=t;i++) x+=(c[i]-'a')*power[i];
 50     if (first[x]) for (i=first[x];i;i=hs[i].next)
 51     {
 52         t=len-loc;
 53         if (t<=0) return;
 54         for (x=1;x<=t;x++) if (hs[i].s[x]!=c[loc+x]) break;
 55         if (x>t) return;
 56     }
 57     if (!(hs[++top].next=first[x])) g[++tg]=x;
 58     first[x]=top;
 59     x=0;
 60     for (i=loc+1;i<=len;i++) hs[top].s[++x]=c[i];
 61     ans++;
 62 }
 63 int main()
 64 {
 65     int la,lb,i,j,t,x,k,l;
 66     char c[N];
 67     scanf("%s\n%s\n%s\n",&s,&a,&b);
 68     ls=strlen(s);
 69     la=strlen(a);
 70     lb=strlen(b);
 71     kmp(a,la);
 72     for (i=1;i<=top;i++) fa[f[i]]=1;
 73     kmp(b,lb);
 74     for (i=1;i<=top;i++) fb[f[i]]=1;
 75     t=ls-la-lb;
 76     for (j=1;j<=t;j++)
 77     {
 78         for (i=1;i<=tg;i++) first[g[i]]=0;
 79         top=tg=0;
 80         for (i=0;i<=t-j;i++)
 81         {
 82             x=i+j+la;
 83             if (fa[i]&&fb[x])
 84             {
 85                 l=0;
 86                 for (k=i+la;k<x;k++) c[++l]=s[k];
 87                 hash(c,j);
 88             }
 89         }
 90     }
 91     for (i=max(la-lb-1,0);i<=la;i++)
 92     {
 93         j=i;
 94         while (a[j]==b[j-i]) j++;
 95         if (a[j]!='\0') continue;
 96         for (j=0;j<i;j++) c[j]=a[j];
 97         for (k=0;k<lb;k++,j++) c[j]=b[k];
 98         kmp(c,j);
 99         if (top) ans++;
100     }
101     printf("%d\n",ans);
102     return 0;
103 }

 

  據說可以用KMP或者EXKMP做orz

  還有我的程序在 Hash 取別的位數的時候會 WA 幾個點(而且還是不同的點??)

  求助大神能解釋一下orz(再次體現我的蒟蒻)

  注:該題為原創題,可購買數據,價格 RMB 2.0

  如需購買在這 Get 聯系方式  http://www.cnblogs.com/hadilo/p/5932395.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM