1. 程式人生 > >字串學習總結(Hash & Manacher & KMP)

字串學習總結(Hash & Manacher & KMP)

# 前言 終於開始學習新的東西了,總結一下字串的一些知識。 ## NO.1 字串雜湊(Hash) ### 定義 即將一個字串轉化成一個整數,並保證字串不同,得到的雜湊值不同,這樣就可以用來判斷一個該字串是否重複出現過。 所以說$Hash$就是用來求字串是否相同或者包含的。(包含關係就可以列舉區間,但是通常用$KMP$,~~不會真的有人用看臉的$Hash$做字串匹配吧,不會吧不會吧~~)。 ### 實現 實現方式也是比較簡單的,其實就是把一個字串轉化為數字進行比較,到這裡可能有人就會說,直接比較長度和$ASCII$碼不就行了,也是轉化成數字啊~~(放屁)~~。這樣顯然是不行的,就好比說"ab"和“ba“,這兩個顯然不一樣,但是如果按上邊說的進行比較就是一樣的,這樣就錯了,所以我們要換一種方式:改變一下進位制。 如果是一個純字串的話,那麼我們應該把進位制調到大於$131$,因為如果小於,就不能給每一種的字元一個值,那麼正確性也就無法保證了。所以取一個$233$,合情合理,~~還很sao(逃~~。因為這個值至少能保證不會炸。我們求出來每個字串對應的數字,然後進行比較就好了。 對於雜湊而言,我們認為對一個數取模後一樣,那麼就是一樣的,所以可以偷點懶,也就是自然溢位,使用$unsigned\ long\ long$,相當於自動對$2^{64}$取模,然後進行比較即可,當然,可以自己背一個$10^{18}$的質數進行取模(畢竟也是能卡的,也不知道哪個毒瘤會卡),各有優缺點。 ### 程式碼 ```cpp ull Hash(char s[]){//ull自然溢位 ull res = 0; int len = strlen(s); for(int i=0;i>s; int len = strlen(s); sum[0] = (ull)s[0]; for(int i=1;i>l>>r>>s>>t; len = r-l+1;//計算第幾位來乘以進位制,pw陣列提前可以快速冪處理好 if(sum[r] - sum[l-1]*pw[len] == sum[t]-sum[s-1]*pw[len])printf("YES\n");//如果這樣計算出來值相等就合法 else printf("NO\n"); } ``` ### 模板例題 [字串雜湊](https://www.luogu.com.cn/problem/P3370) ### 例題程式碼 ```cpp #include
using namespace std; #define ull unsigned long long const ull mod = 1926081719260817; const int maxn = 1e4+10; ull base = 233; int a[maxn]; char s[maxn]; ull Hash(char s[]){ ull res = 0; int len = strlen(s); for(int i=0;i>s; a[i] = Hash(s); } int ans = 1; sort(a+1,a+n+1); for(int i=1;i using namespace std; const int maxn = 11e6; char s[maxn]; int Manacher(char s[]){ int len = strlen(s); if(len == 0)return 0;//長度為0就return int len1 = len * 2 + 1; char *ch = new char[len1];//動態陣列 int *par = new int[len1]; int head = 0; for(int i=0;i -1&& ch[i + par[i]] == ch[i - par[i]]){//暴力匹配 par[i] ++ ; } if(i + par[i] > R){//如果超過右邊界就更新 R = i + par[i]; C = i; } Max = max(Max,par[i]);//更新最大半徑 } delete[] ch;//清空動態陣列 delete[] par; return Max - 1;//因為這個是添了字元的最大回文半徑,所以迴文串的最長是它-1 } int main(){ cin>>s; cout<
using namespace std; const int maxn = 1e6+10; char a[maxn],b[maxn]; int kmp[maxn]; int main(){ cin>>a+1>>b+1; int lena = strlen(a+1); int lenb = strlen(b+1); int j = 0; for(int i=2;i<=lenb;++i){//自己跟自己匹配處理出kmp陣列 while(j && b[i] != b[j+1]){ j = kmp[j]; } if(b[i] == b[j+1])j++; kmp[i] = j; } j = 0; for(int i=1;i<=lena;++i){ while(j && a[i] != b[j+1]){ j = kmp[j]; } if(a[i] == b[j+1])j++; if(j == lenb){//匹配完了就輸出位置 printf("%d\n",i-lenb+1); j = kmp[j];//返回失配位置 } } for(int i=1;i<=lenb;++i){ printf("%d ",kmp[i]); } return 0; } ```