字串學習總結(Hash & Manacher & KMP)
阿新 • • 發佈:2020-07-22
# 前言
終於開始學習新的東西了,總結一下字串的一些知識。
## 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;
}
```