2021.7.16 字串講解
By Zhang_RQ
雜湊
對於一個長度為 n 的字串,其雜湊值為 \(\sum\limits_{i=1}^ns_i\times base^{n-i}\bmod p\),其中 \(base\) 和 \(p\) 自選
判斷兩個字串的一個方法是直接判斷雜湊值是否相等(雖然說有概率出錯)。
可以快速算出一個字串任意子串的雜湊值。
具體而言,預處理出字首雜湊值,然後減一減,乘 \(base\) 的若干次逆元就可以了。
在 \(10^5\) 級別時可能會出現雜湊衝突,可以使用雙模數。
有種特殊的模數是自然溢位。
KMP
線上性時間中完成字串匹配的演算法
內容:給定一個源串和模式串,求出模式串在源串中出現的次數及出現的位置。
核心思想:減少暴力匹配時的浪費的資訊,利用 \(nxt\) 陣列進行優化
\(nxt_i\) 的定義:對於以 \(i\) 結尾的字首,滿足最大的"字首等於字尾"的長度。
\(nxt\) 求法:\(nxt_i=nxt_{i-1}\) 新加入的字元匹配 \(+1\) ,否則跳 \(nxt\)
匹配做法:暴力匹配,失配時跳 \(nxt\) 而不是從頭開始。
for(ri i=1,j=0;i<=n;i++){ while(j&&s2[j+1]!=s1[i]) j=nxt[i]; if(s2[j+1]==s1[i]) j++; if(j==m){ ans.push_back(i-m+1); j=nxt[j]; } }
Trie樹
把節點作為狀態,把字母放到邊上。
用法:維護若干串,查詢一個串是否是這些串的字首。
void insert(char *s,int id){
int x=rt;
for(ri i=1;s[i];i++){
if(!son[x][s[i]-'a'])
son[x][s[i]-'a']=++cnt;
x=son[x][s[i]-'a'];
}
nd[id]=x;
}
01Trie:把數字當成二進位制串插到Trie裡。可以實現不少有趣的功能,比如查一個數的前驅後繼(有點像平衡樹?)。一個比較經典的應用是維護一個數集,每次查詢時給定一個數字,要求從數集中選出來一個數,最大化或最小化兩個數的異或和
AC自動機
可以理解為在多個串上的KMP。利用Trie樹來維護這些串,nxt陣列變為fail指標。
Fail指標的構造思想:
可以直接構造 trie 圖以進行多串匹配。
Trie圖構建的程式碼:
Manacher
用於求解最長迴文子串的演算法,線性。
思想:利用之前的已知資訊來優化:
- 維護當前最長的迴文串和其迴文中心
- 對於一個新加入的位置,從關於會問中心對稱的位置繼承答案。
- 這樣每次更新答案的時候都是本質不同的迴文串,複雜度自然就是線性的。
在字元之間新增 '#' 以處理迴文中心不在字元上的情況。