rust-algorithms:17-BWT

godme發表於2022-07-29

詳細可以看一下大佬的文章,這裡主要說一下LF的對應關係。

  • F和L
    作為FirstLast,兩者的區分十分明顯。
    就算我們不知道兩者之間的資料,但是單看FL,我們一定知道邊界情況。

  • 內容
    因為全部會迴圈一遍,因此在F(L)中出現過的資料,一定遍歷了全部元素。

  • F的固有順序
    因為F本身就是排序後的產物,因此只要有亂序後的資料,隨時能夠得到F
    因此,最後保留L,其實是可以間接算出F的。

  • 位置資訊的保留
    rust-algorithms:17-BTW
    因為插入的#或者$是完全小於任意字元的字元序的。
    因此,迴圈字元的排序,即使是相同的字元,排序過後也隱含的保留了字元出現的順序。

  • 字串的恢復
    rust-algorithms:17-BTW
    我們固然已經不知道中間內容,但是透過下面的幾點,可以毫無歧義的進行恢復

    • 透過L準確恢復F,並且L第一個字元肯定是最後一個
    • 透過FL的對應關係,能夠明確對映出首尾關係
    • 透過F隱含的字元序,恢復原先的字元排列順序

    整體就是:

    • 將整體的字元序隱藏在F
    • F隱藏在L中,切L自帶字元資訊
    • 透過L恢復F
    • 透過F的字元序,L的字元資訊、LF的對照關係進行恢復

不管是使用#還是$,還是其他的辦法,都存在一個問題

我們使用什麼字元來保證最小字元序

這將引起兩個問題

  • 我們需要先遍歷一遍
  • 字串中包含最小字元序的字元

換句話說,我們為什麼需要最小字元序的字元?
是因為缺少了這個最小字元序的字元就無法構建出如此條件的情況?
事實是,選取最小字元序字元,可以保證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 協議》,轉載必須註明作者和本文連結