1. 程式人生 > 其它 >2021.7.16 字串講解

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

用於求解最長迴文子串的演算法,線性。

思想:利用之前的已知資訊來優化:

  1. 維護當前最長的迴文串和其迴文中心
  2. 對於一個新加入的位置,從關於會問中心對稱的位置繼承答案。
  3. 這樣每次更新答案的時候都是本質不同的迴文串,複雜度自然就是線性的。

在字元之間新增 '#' 以處理迴文中心不在字元上的情況。