String

程度★ 難度★

String

「字串」由一串字元所構成。例如aaabbbccc、48Dfua@~!0Hrb、m、How are you?等都是字串。有個特例是空字串:一個字元都沒有的字串,通常標記為ф。

字串的長度就是一個字串擁有的字元數目。空字串就是長度為零的字串。

Character

「字元」是字串的基本單元,一個字串當中的每個符號皆是字元,例如字串How are you?的字元依序為:H、o、w、 、a、r、e、 、y、o、u、?。字串How are you?的第一個字元為英文字母H,第三個字元為英文字母w,第四個字元為空白符號 。最後一個字元為問號?。

中文句子的各個文字也都是字元。例如「你好嗎?」的第二個字元是「好」,第四個字元是全形問號「?」。

ASCII Table列出了電腦中基本的128種字元,包括大小寫英文字母、標點符號阿拉伯數字、數學運算符號、其他雜七雜八的符號等等。

Substring

「子字串」是字串當中的一段字串。例如algo的各個子字串為ф, a, l, g, o, al, lg, go, alg, lgo, algo。

Prefix

「前綴」。一個字串的開頭幾個字元所構成的子字串(砍去末端幾個字元),為原字串的前綴。例如taiwan的各個前綴是ф, t, ta, tai, taiw, taiwa, taiwan這七個前綴,ф是指空字串。

Suffix

「後綴」。一個字串的末端幾個字元所構成的子字串(砍去開頭幾個字元),為原字串的後綴。例如taiwan的各個後綴是ф, n, an, wan, iwan, aiwan, taiwan這七個後綴,ф是指空字串。

Subsequence

「子序列」是字串當中由左到右抽取字元所構成的字串。例如algo的各個子序列為ф, a, l, g, o, al, ag, ao, lg, lo, go, alg, alo, ago, lgo, algo。

字串與數列

字串裡面的字元,是有限多種;數列裡面的數字,有無限多種。屏除這項差異之後,字串與數列是完全相同的,字串可以視作數列、數列可以視作字串。

String資料結構: Array

程度★ 難度★

使用Array儲存一個字串

把字元依序填入陣列,最後用個特殊符號標記字串結尾。

要不然也可以記錄最後一個字元的索引值,這樣就不用加特殊符號。紀錄字串長度也是可以的,數值比前者多一。

缺點是插入字串比較慢,需要搬動插入點之後的所有字元。

String資料結構: Rope

程度★ 難度★★★

Rope

【待補文字】

a balanced binary tree whose external node has characters.
string concatenation: O(1)
string indexing: O(logN)
get substring: string indexing + string anti-concatenation = O(logN)
string traversal: O(N)

大量String資料結構: Trie

程度★ 難度★★★

Trie

儲存大量字串的資料結構,可以想作是一部字典。Trie是一棵特別的樹,每一層的節點以indexing的方式依序紀錄字串的各個字元。一棵Trie可以想作是二維的indexing。

舉一個簡單的例子。假設字串中的字母只有abcde五種。現在要儲存abc這個字串:

由樹根往下,每一層的節點依序對應到字串中每一個字元。多出來的樹葉,可以想成是類似於'\0'的東西。現在再儲存aeb這個字串:

有相同開頭的字串,就會歸類在一起。這種儲存字串的方式,與查字典的模式非常相像,可以減低檢索單字的困難度。相信各位對Trie的儲存模式已經駕輕就熟了。

設計Trie的節點

ASCII一共有128個不同的字元,所以一個節點只需要一條128格的陣列就可以了。

如果遇到abc和abcde這種一個字串是另一個字串的字首的例子,就無法輕易的以樹葉來判斷字串結束了沒。所以必須再用一個變數來記錄:從樹根到目前的節點是不是已經形成了一個字串。

如果遇到abcde和abcde這種相同字串一樣的例子,則可以用一個變數進行累計。

特例:空字串

值得一提的是,一棵Trie可以儲存空字串、空字串可以存入一棵Trie。

Trie的節點

增加一個字串

時間複雜度是O(S),其中S是字串的長度。

尋找一個字串(判斷字串存不存在)

時間複雜度是O(S),其中S是字串的長度。

依照順序印出所有字串

使用遞迴走訪每個節點。簡單來說就是DFS。時間複雜度等同於Trie上的節點個數。

釋放記憶體空間

寫了new而不寫delete是大逆不道的事情!一定要記得寫!

結論

Trie的優點就是處理速度奇快無比,字串有多少字元,就花多少時間,到達了速度的極限;缺點就是耗費大量記憶體,陣列中會有許多空格,樹上也會有許多空樹葉。各位有興趣的話可以數數看一個節點用了多少byte的記憶體,然後再數一數一棵Trie有多少個節點,粗估一下一棵Trie所需要的記憶體空間。

Trie的幾種圖示法

UVa 902 10226 10391 10745

延伸閱讀:Compressed Trie

去掉沒有分岔、連成一直線的節點。每個節點增加一個數字,紀錄是第幾個字元開始分岔。

去掉節點之後,字串資訊不完整,只好在樹葉裡儲存完整字串。每個節點增加一個指標,紀錄要參考哪一個葉子的字串開頭。

大量String資料結構: Ternary Search Tree

程度★ 難度★★★

Ternary Search Tree

【待補文字】

a ternary tree whose every node has 1 character.
left-child: lexi.-smaller string, original index.
right-child: lexi.-larger string, original index.
mid-child: next character of current string, index + 1.

a ternary search tree is equivalent to 'a binary tree of strings.'
it's just a better presentation.