詳細可以看一下大佬的文章,這裡主要說一下LF的對應關係。
F和L
作為First
和Last
,兩者的區分十分明顯。
就算我們不知道兩者之間的資料,但是單看F
和L
,我們一定知道邊界情況。內容
因為全部會迴圈一遍,因此在F
(L
)中出現過的資料,一定遍歷了全部元素。F的固有順序
因為F
本身就是排序後的產物,因此只要有亂序後的資料,隨時能夠得到F
。
因此,最後保留L
,其實是可以間接算出F
的。位置資訊的保留
因為插入的#
或者$
是完全小於任意字元的字元序的。
因此,迴圈字元的排序,即使是相同的字元,排序過後也隱含的保留了字元出現的順序。字串的恢復
我們固然已經不知道中間內容,但是透過下面的幾點,可以毫無歧義的進行恢復- 透過
L
準確恢復F
,並且L
第一個字元肯定是最後一個 - 透過
F
和L
的對應關係,能夠明確對映出首尾關係 - 透過
F
隱含的字元序,恢復原先的字元排列順序
整體就是:
- 將整體的字元序隱藏在
F
中 - 將
F
隱藏在L
中,切L
自帶字元資訊 - 透過
L
恢復F
- 透過
F
的字元序,L
的字元資訊、L
和F
的對照關係進行恢復
- 透過
不管是使用#
還是$
,還是其他的辦法,都存在一個問題
我們使用什麼字元來保證最小字元序
這將引起兩個問題
- 我們需要先遍歷一遍
- 字串中包含最小字元序的字元
換句話說,我們為什麼需要最小字元序的字元?
是因為缺少了這個最小字元序的字元就無法構建出如此條件的情況?
事實是,選取最小字元序字元,可以保證F
排序;
進而保證L
開端就可以作為解析的開始入門符號。
這裡不可避免的涉及上面兩個問題,能否有更好的辦法完成這個工作呢?
有!那就是將作為解析的開端作為編碼結果的一部分進行記錄。
這裡引申出一個有意思的問題:是否只能夠存在一個解析開端。
誠然,歷經一個迴圈遍歷週期,迴圈串必定至少一次的和原串重合
我們將第n
個週期的初始順序記錄,都必定能夠一個無誤的開端。
但是在一次迴圈遍歷週期中,如果變體串和原串相等,也就是出現了迴環串
是否可以作為解析開端呢?
答案是必然可以的,因為
- 我們只是需要一個開端,無誤的開端
- 我們需要的不是原來的字串,而是和原字串相等的字串
正如我們恢復的資料不是原來的資料,而是相同的資料
載體的不等,並不妨礙資料的相同
pub fn encode(input: String) -> (String, usize) {
let len = input.len();
let mut table = Vec::<String>::with_capacity(len);
// 字元移位迴圈
for i in 0..len {
table.push(input[i..].to_owned() + &input[..i]);
}
// 排序M
table.sort_by_key(|a| a.to_lowercase());
let mut encoded = String::new();
let mut index: usize = 0;
for (i, item) in table.iter().enumerate().take(len) {
// 構造L
encoded.push(item.chars().last().unwrap());
// 解析開端,可以只賦值一次,迴環子串也沒問題
if item.eq(&input) {
index = i;
}
}
(encoded, index)
}
pub fn decode(input: (String, usize)) -> String {
let len = input.0.len();
let mut table = Vec::<(usize, char)>::with_capacity(len);
// L序號標記,後續對映表可查
// 不僅是字元,還有順序要求,不能直接使用Map
// table是L和F二合一,目的是確定L的解析順序,和F字元無關,確定對映關係即可
for i in 0..len {
table.push((i, input.0.chars().nth(i).unwrap()));
}
// 恢復F,帶序號
table.sort_by(|a, b| a.1.cmp(&b.1));
let mut decoded = String::new();
let mut idx = input.1;
for _ in 0..len {
// 根據L填充資料
decoded.push(table[idx].1);
// 反查F確定順序
idx = table[idx].0;
}
decoded
}
測序什麼的我完全不懂,但是經過BWT演算法計算之後的最大特徵就是:相同字元緊密排列。
因為F
是排序的,迴圈遍歷的首尾呼應,L
中的資料也是緊密字元序。
對於重複字元而言,更利於壓縮,這也造就了它通用壓縮的使用範圍。```actionscript
pub fn decode(input: (String, usize)) -> String {
let len = input.0.len();
let mut table = Vec::<(usize, char)>::with_capacity(len);
// L序號標記,後續對映表可查
// 不僅是字元,還有順序要求,不能直接使用Map
// table是L和F二合一,目的是確定L的解析順序,和F字元無關,確定對映關係即可
for i in 0..len {
table.push((i, input.0.chars().nth(i).unwrap()));
}
// 恢復F,帶序號
table.sort_by(|a, b| a.1.cmp(&b.1));
let mut decoded = String::new();
let mut idx = input.1;
for _ in 0..len {
// 根據L填充資料
decoded.push(table[idx].1);
// 反查F確定順序
idx = table[idx].0;
}
decoded
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結