豆包MarsCode AI 刷题

107 阅读6分钟

Base32编码和解码问题

题目

你需要实现一个 Base32 的编码和解码函数。

相比于 Base32,你可能更熟悉 Base64,Base64 是非常常见的用字符串形式表示二进制数据的方式,在邮件附件、Web 中的图片中都有广泛的应用。

Base32 是 Base64 的变种,与 Base64 不同的地方在于 Base64 以 6 bit 为一组作为索引,而 Base32 以 5 bit 为一组作为索引,每一组用一个 ASCII 字符表示。Base 64 总共需要 64 个字符表示,而 Base32 则只需要 32 个字符表示。

Base32 的编码流程如下:

  • 对二进制数据进行预处理:如果二进制数据的 bit 数目不是 5 的倍数的话,在末尾补 0 直至为 5 的倍数。
  • 以 5 bit 为一组进行分组。
  • 将每一组的 5 bit 二进制转换为索引(0 - 31)。
  • 在索引 - 字符转换表中查询索引对应的字符。
  • 根据原始二进制数据的 bit 数目除以 40 后的余数,确定末尾需要补 + 的数目。
  • 如果原始二进制数据 bit 数目除以 40 后的余数是 0 的话,不需要补 +
  • 如果余数是 8,补 6 个 +
  • 如果余数是 16,补 4 个 +
  • 如果余数是 24,补 3 个 +
  • 如果余数是 32,补 1 个 +

Base32 的索引 - 字符转换表如下:

索引:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

字符:9 8 7 6 5 4 3 2 1 0 m n b v c x z a s d f g h j k l p o i u y t

你需要对字符串rawStr进行编码,并对encodedStr进行解码。

思路

首先是些许准备工作
首先建立二进制与编码字符的映射,编码用数组表示,解码用哈希表表示

private static final char[] encodingMap = new char[]{'9', '8', '7', '6', '5', '4', '3', '2', '1', '0',
        'm', 'n', 'b', 'v', 'c', 'x', 'z', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'p', 'o', 'i', 'u', 'y', 't'};

private static final Map<Character, Integer> decodingMap = new HashMap<>();

static {
    for (int i = 0; i < encodingMap.length; i++) {
        decodingMap.put(encodingMap[i], i);
    }
}

准备工作结束,接下来考虑编码与解码
对于编码,直接模拟即可
对于解码,首先将问题的encodedString+分割得到多个encoded,根据编码字符串encoded可以得到对应二进制序列encodedBits,这个二进制序列是将原字符串rawStr的二进制表示originalBits末位补0得到的,且补的0的数量不超过4,而原字符串的二进制表示的长度必为8的倍数,所以去掉encodedBits末尾encodedBits % 8位,即得到originalBits

代码

最后附上代码

public class Main {
    private static final char[] encodingMap = new char[]{'9', '8', '7', '6', '5', '4', '3', '2', '1', '0',
            'm', 'n', 'b', 'v', 'c', 'x', 'z', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'p', 'o', 'i', 'u', 'y', 't'};

    private static final Map<Character, Integer> decodingMap = new HashMap<>();

    static {
        for (int i = 0; i < encodingMap.length; i++) {
            decodingMap.put(encodingMap[i], i);
        }
    }

    public static String solution(String rawStr, String encodedString) {
        String ret = encode(rawStr);
        ret = ret.concat(":");
        for (String encoded :
                encodedString.split("\+")) {
            if (encoded.length() > 0) {
                ret = ret.concat(decode(encoded));
            }
        }
        return ret;
    }

    public static void main(String[] args) {
        System.out.println(solution("foo", "b0zj5+++").equals("bljhy+++:bar"));
        System.out.println(solution("The encoding process represents 40-bit groups of input bits as output strings of 8 encoded characters.  Proceeding from left to right, a 40-bit input group is formed by concatenating 5 8bit input groups. These 40 bits are then treated as 8 concatenated 5-bit groups, each of which is translated into a single character in the base 32 alphabet.  When a bit stream is encoded via the base 32 encoding, the bit stream must be presumed to be ordered with the most-significant- bit first. That is, the first bit in the stream will be the high- order bit in the first 8bit byte, the eighth bit will be the low- order bit in the first 8bit byte, and so on.", "bljhy+++b0zj5+++").equals("maf3m164vlahyl60vlds9i6svuahmiod58l3mi6sbglhmodfcbz61b8vb0fj1162c0jjmi6d58jhb160vlk2mu89b0fj1il9b4ls9oogcak2mu89cvp25pncbuls9oo359i79lncbvjh1ln558ahzknsb4aj1lnscbj7917zc0jh3ln4bafhill9bll3yo09vashbu89cajs9id0buf21n89b5z61b8vb0fj1160vlk2mu89bul3yunz58fj3163vul3pln558a2s166vuj33knfbgj37u60vlds9v0928a3su89v4j29unf58dj5oogc8lsi17fv8sj3l093zk79kd0cals9knsbfz21p64vkz21id4b4p3ml89b4ls9c89bvjhiko8cashiknfbgs79v0vb0fj1162c0jjmi6d4zz3mkn6v9z3yla9cuf3sko158fj316fc0zhiiobb4p3ml89v4j21ol9b5z23pncbuh3m166v8zj5kn6casj5160vkz21p6458a37io459ld5168vak3zkn7bgp7i189muf3moa9b5z35pnf58lj1id4b4hs9pnd58shikoxbash116hv4zs9u61bfz35kndbfz63ba9bgj33oo5v4j3cn89caf3m167v4p79iofc0sh7o09vgpj3u89b0ss9i6sbgljmon4bzz21ol9b0ss9oosbasj5ln558ohsu6158p3zl09vgjj3u8vcvfhcod0blfh3kncczhs9kd0czz3bpnscvp7i17fv8zj1160cbh79u61bfz3bpnscvp79kd0czz3soa9caf3m16dcal3mknv58ohso6b58a3m16fv8ss9p60buf7p16xc0s3mia9b0fj1160vkz21p6458d3siddczz6zkd0czz35ynfbfh79u61bfz3mpn2v8p3z167v4p79uo0vah79kd458p3zl09vajjcn09vul31lns58a3su89v4j79u61bfz3bpnscvp79c67v4p79kdlcassk168vls79iox58jhinz+:foobar"));
    }

    private static String decode(String encodedString) {
        int originalBitsLength = encodedString.length() * 5 - (encodedString.length() * 5) % 8;
        boolean[] bits = new boolean[originalBitsLength];
        for (int i = 0; i < encodedString.length(); i++) {
            int index = decodingMap.get(encodedString.charAt(i));
            for (int j = 0; j < 5; j++) {
                if (i * 5 + 4 - j < bits.length) {
                    bits[i * 5 + 4 - j] = (index & 1) == 1;
                }
                index >>= 1;
            }
        }
        return new String(getBytes(bits));
    }

    private static String encode(String rawStr) {
        boolean[] bits = getBits(rawStr.getBytes(StandardCharsets.US_ASCII));
        String encoded = "";
        int mod = bits.length % 40;
        for (int i = 0; i < bits.length; i += 5) {
            int index = 0, increase = 1;
            for (int j = 4; j >= 0; j--) {
                if (i + j < bits.length && bits[i + j]) {
                    index += increase;
                }
                increase *= 2;
            }
            encoded = encoded.concat(String.valueOf(encodingMap[index]));
        }
        if (mod == 8) {
            encoded = encoded.concat("++++++");
        } else if (mod == 16) {
            encoded = encoded.concat("++++");
        } else if (mod == 24) {
            encoded = encoded.concat("+++");
        } else if (mod == 32) {
            encoded = encoded.concat("+");
        }
        return encoded;
    }

    private static boolean[] getBits(byte[] bytes) {
        boolean[] booleans = new boolean[bytes.length * 8];
        byte one = 1;
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            for (int j = 0; j < 8; j++) {
                booleans[i * 8 + 7 - j] = (b & one) == 1;
                b >>= 1;
            }
        }
        return booleans;
    }

    private static byte[] getBytes(boolean[] bits) {
        byte[] bytes = new byte[bits.length / 8];
        for (int i = 0; i < bits.length; i += 8) {
            byte sum = 0, increase = 1;
            for (int j = 0; j < 8; j++) {
                if (bits[i + 7 - j]) {
                    sum |= increase;
                }
                increase <<= 1;
            }
            bytes[i / 8] = sum;
        }
        return bytes;
    }
}