試題 算法訓練 Bit Compressor

題目:

試題 算法訓練 Bit Compressor算法

資源限制
時間限制:1.0s 內存限制:256.0MB
問題描述
  數據壓縮的目的是爲了減小存儲和交換數據時出現的冗餘。這增長了有效數據的比重並提升了傳輸速率。有一種壓縮二進制串的方法是這樣的:
  將連續的n個1替換爲n的二進制表示(注:替換髮生當且僅當這種替換減小了二進制串的總長度)
  (譯者注:連續的n個1的左右必須是0或者是串的開頭、結尾)
  好比:11111111001001111111111111110011會被壓縮成10000010011110011。原串長爲32,被壓縮後串長爲17.
  這種方法的弊端在於,有時候解壓縮算法會獲得不止一個可能的原串,使得咱們沒法肯定原串到底是什麼。請你寫一個程序來斷定咱們可否利用壓縮後的信息來肯定原串。給出原串長L,原串中1的個數N,以及壓縮後的串。
  L<=16 Kbytes,壓縮後的串長度<=40 bits。
輸入格式
  第一行兩個整數L,N,含義同問題描述
  第二行一個二進制串,表示壓縮後的串
輸出格式
  輸出"YES"或"NO"或"NOT UNIQUE"(不包含引號)
  分別表示:
  YES:原串惟一
  NO:原串不存在
  NOT UNIQUE:原串存在但不惟一
樣例輸入
樣例1:
32 26
10000010011110011
樣例2:
9 7
1010101
樣例3:
14 14
111111
樣例輸出
樣例1:YES
樣例2:NOT UNIQUE
樣例3:NO數組

算法題型:類解壓縮
解題方法:遞歸求解
解題思路:壓縮後的字符串已知,能夠對壓縮後的字符串進行從頭至尾遍歷,用指針的移動(Java中就是數組的索引) 來討論全部狀況。每當指針指向的字符爲'1'時就能夠展開進一步的遍歷,即將當前索引做爲字符串的開始索引,結束 索引初始化爲開始索引+1,而後依次遞增到壓縮後的字符串的長度,在這個過程當中,若是發現結尾索引所指向的字符爲 '1'的話,就跳過當前循環,由於根據題意可知,先後都爲1的字符串不符合壓縮條件,因此解壓縮時就不用考慮該狀況。 也就是說除了先後都爲1的這種狀況外,還剩下三種狀況是咱們須要寫算法時須要考慮的: 例如:
1) 111110
2) 011110
3) 011111
這裏尤爲要注意的一點就是當截取的字符串是「11」,也就是n = 3的時候,由於3這個數很是特別,又由於題目的要求, 當n = 3時,原字符串可能爲「111」,也可能爲「11」,因此咱們這裏須要分兩種狀況討論,而若是當指針指向的字符爲 '0'時則指針向後移動一位,當前字符串的總長度+1,當前字符串的含「1」個數不變繼續向下遞歸。重複該過程,直到當前索引等於壓 縮後的字符串的總長度時,結束第一次遞歸,而後又繼續下一次遞歸,直到全部狀況都討論完畢,程序中止。算法結果得 到輸出。

Java代碼:spa

public class Main {
    // 原字符串長
    private static int L;
    // 原字符串1的個數
    private static int N;
    // 壓縮後的字符串
    private static String str;
    // 知足條件的原字符串個數
    private static int count;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        L = input.nextInt();
        N = input.nextInt();
        str = input.next();
        division(0, 0, 0);
        if (count > 1) {
            System.out.println("NOT UNIQUE");
        } else if (count < 1) {
            System.out.println("NO");
        } else {
            System.out.println("YES");
        }
    }

    /**
     * 32 26
     * 10000010011110011
     * @param curIndex 當前壓縮後字符串的索引
     * @param curStrLen 當前字符串的長度
     * @param curOneNum 當前字符串中1的個數
     */
    public static void division(int curIndex, int curStrLen, int curOneNum) {
        // 出口
        if (curIndex == str.length()) {
            if (curStrLen == L && curOneNum == N) {
                count++;
            }
            return;
        }
        if (curStrLen > L) {
            return;
        }
        if (curOneNum > N) {
            return;
        }

        // 第一個開始字符是1的話,那麼就從這個1開始,向後依次遍歷
        if (str.charAt(curIndex) == '1') {
            for (int i = curIndex; i < str.length(); i++) {
                // 若是是「1xxxx1」則不用判斷,必定沒有這種狀況
                /*
                    根據題意:能壓縮的只有三種狀況
                        一、111110
                        二、011110
                        三、011111
                 */

                // 開頭和結尾都是1那麼就直接跳過這種狀況
                if (i + 1 < str.length() && str.charAt(i + 1) == '1') {
                    continue;
                }
                int n = getD(str.substring(curIndex, i + 1));
                // 注意 「11」 的原字符串多是 「111」 或者 「11」 因此n = 3時要多分一種狀況
                /*
                  若是長度爲3,原串多是111
                  但若是原串是11,就不須要轉換他
                  因此咱們不清楚結果中的11是通過轉換的,仍是說原來就是這樣的
                  因此分兩種狀況:
                 */
                if (n == 3) {
                    // 原串爲「11」
                    division(i + 1, curStrLen + 2, curOneNum + 2);
                }
                division(i + 1, curStrLen + n, curOneNum + n);
            }
        } else {
            // 若是第一個字符不是1,而是零,則直接指針向後移一位,當前字符串的長度+1
            division(curIndex + 1, curStrLen + 1, curOneNum);
        }
    }

    /**
     * 返回對應二進制數的十進制數
     * @param binStr
     * @return
     */
    public static int getD(String binStr) {
        int total = 0;
        for (int i = 0; i < binStr.length(); i++) {
            if (binStr.charAt(i) == '1') {
                total += (long) Math.pow(2, (binStr.length() - 1 - i));
            }
        }
        return total;
    }
}
相關文章
相關標籤/搜索