字串演算法

hash

雜湊

hash function

字串轉換成數字

fast

hash

function

48763

用途

\(O(1)\)判斷兩字串是否相同

隱憂:hash須注意碰撞(不同子串輸出相同數字)

因為字串有無限多種,沒辦法在變數範圍對應到所有可能

Rolling Hash

將字串視為多項式

\( (s_0\times p^{n-1}+s_1\times p^{n-2}+...+s_{n-2}\times p^1+s_{n-1}\times p^0) \mod m \)

字串為\(s\)

p是一個夠大的質數(可以=29, 257)

m為很大的質數(通常=\(10^9+7\))

區間hash -> 前綴hash相減

建前綴hash:\(h(i)=(h(i-1)*p^1+s_i*p^0)\mod m\)

i~j區間hash:

 

\((h(j)-h(i-1)\times p^{j-i})\mod m\)

trie字典樹

用途:有效率的查詢一個字是不是在一堆字裡面

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

i

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

i

l

建樹:如果一些字前綴都一樣,則一樣的部分合再一起

加入while

i

l

e

z-algorithm

\(O(n)\)確定性字串匹配 (不會碰撞)

建一z陣列\(z[i]\),代表從\(s_i\)開始跟\(s\)的最長共同前綴

(z[0]為未定義)

       a a b c a a b
z[] -  x 1 0 0 3 1 0

如何運用在字串匹配?

ex:段落是\(p\),要匹配的是\(s\)

對\(s\$p\) (兩字串合併中間插入$) 建一個z陣列

當\(z[i]=|s|\)時即為匹配

p=baabaa, s=aab

      a a b $ b a a b a a
z[] = x 1 0 0 0 3 1 0 2 1

如何建z陣列?

首先維護\(l\), \(r\),代表面前以匹配最右的區間

 

對於當前位置\(i\),有兩個情況

  • 若\(i>r\),直接暴力計算\(z[i]\),並更新\(l, r\)
  • 若\(i\le r\),\(z[i] = min(z[i-l], r-i+1)\)作為初始值,之後暴力擴展\(z[i]\)
l=4, r=6, i=5

       a a b c a a b
z[] -  x 1 0 0 3
l=5, r=7, i=6

       a a a a b a a a c
z[] -  x 3 2 1 0 3

均攤\(O(n)\)

void match(vector<int> &z, string s){
    int len=s.size();
    z[0]=0;
    int l=0, r=0;
    rep(i,1,len){
        if(i<=r) z[i]=min(z[i-l], r-i+1);
        while(i+z[i]<len && s[z[i]] == s[i+z[i]]) z[i]++;
        if(i+z[i]-1>r) l=i, r=i+z[i]-1;
    }
}

kmp

備註 7:32

if this two not the same, next=0

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int f[1000010]={0};
 
int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	string a, b;
	cin>>a>>b;
	int lena=a.size(), lenb=b.size();
	// build failure function
	f[0]=-1;
	int j=-1;
	for(int i=1;i<lenb;++i){
		while(j!=-1 && b[j+1]!=b[i]) j=f[j];
		if(b[j+1]==b[i]) j++;
		f[i]=j;
	}
	//match
	int ans=0;
	j=-1;
	for(int i=0;i<lena;++i){
		while(j!=-1 && a[i]!=b[j+1]) j=f[j];
		if(a[i]==b[j+1]) j++;
		if(j==lenb-1) ans++, j=f[j];
	}
	cout<<ans<<"\n";
}

0-based,無法匹配 = -1

字串演算法

By alan lai

字串演算法

  • 163