蓝桥杯——试题 算法训练 Bit Compressor
Java代码:
1 /** 2 * @Author LZP 3 * @Date 2021/2/28 10:45 4 * @Version 1.0 5 * 6 试题 算法训练 Bit Compressor 7 8 9 资源限制 10 时间限制:1.0s 内存限制:256.0MB 11 问题描述 12 数据压缩的目的是为了减少存储和交换数据时出现的冗余。这增加了有效数据的比重并提高了传输速率。有一种压缩二进制串的方法是这样的: 13 将连续的n个1替换为n的二进制表示(注:替换发生当且仅当这种替换减少了二进制串的总长度) 14 (译者注:连续的n个1的左右必须是0或者是串的开头、结尾) 15 比如:11111111001001111111111111110011会被压缩成10000010011110011。原串长为32,被压缩后串长为17. 16 这种方法的弊端在于,有时候解压缩算法会得到不止一个可能的原串,使得我们无法确定原串究竟是什么。请你写一个程序来判定我们能否利用压缩后的信息来确定原串。给出原串长L,原串中1的个数N,以及压缩后的串。 17 L<=16 Kbytes,压缩后的串长度<=40 bits。 18 输入格式 19 第一行两个整数L,N,含义同问题描述 20 第二行一个二进制串,表示压缩后的串 21 输出格式 22 输出"YES"或"NO"或"NOT UNIQUE"(不包含引号) 23 分别表示: 24 YES:原串唯一 25 NO:原串不存在 26 NOT UNIQUE:原串存在但不唯一 27 样例输入 28 样例1: 29 32 26 30 10000010011110011 31 样例2: 32 9 7 33 1010101 34 样例3: 35 14 14 36 111111 37 样例输出 38 样例1:YES 39 样例2:NOT UNIQUE 40 样例3:NO 41 42 算法题型:类解压缩 43 解题方法:递归求解 44 解题思路:压缩后的字符串已知,可以对压缩后的字符串进行从头到尾遍历,用指针的移动(Java中就是数组的索引) 45 来讨论所有情况。每当指针指向的字符为'1'时就可以展开进一步的遍历,即将当前索引作为字符串的开始索引,结束 46 索引初始化为开始索引+1,然后依次递增到压缩后的字符串的长度,在这个过程中,如果发现结尾索引所指向的字符为 47 '1'的话,就跳过当前循环,因为根据题意可知,前后都为1的字符串不符合压缩条件,所以解压缩时就不用考虑该情况。 48 也就是说除了前后都为1的这种情况外,还剩下三种情况是我们需要写算法时需要考虑的: 49 例如: 50 1) 111110 51 2) 011110 52 3) 011111 53 54 这里尤其要注意的一点就是当截取的字符串是“11”,也就是n = 3的时候,因为3这个数非常特别,又因为题目的要求, 55 当n = 3时,原字符串可能为“111”,也可能为“11”,所以我们这里需要分两种情况讨论,而如果当指针指向的字符为 56 '0'时则指针向后移动一位,当前字符串的总长度+1,当前字符串的含“1”个数不变继续向下递归。重复该过程,直到当前索引等于压 57 缩后的字符串的总长度时,结束第一次递归,然后又继续下一次递归,直到所有情况都讨论完毕,程序停止。算法结果得 58 到输出。 59 */ 60 public class Main { 61 // 原字符串长 62 private static int L; 63 // 原字符串1的个数 64 private static int N; 65 // 压缩后的字符串 66 private static String str; 67 // 满足条件的原字符串个数 68 private static int count; 69 70 public static void main(String[] args) { 71 Scanner input = new Scanner(System.in); 72 L = input.nextInt(); 73 N = input.nextInt(); 74 str = input.next(); 75 division(0, 0, 0); 76 if (count > 1) { 77 System.out.println("NOT UNIQUE"); 78 } else if (count < 1) { 79 System.out.println("NO"); 80 } else { 81 System.out.println("YES"); 82 } 83 } 84 85 /** 86 * 32 26 87 * 10000010011110011 88 * @param curIndex 当前压缩后字符串的索引 89 * @param curStrLen 当前字符串的长度 90 * @param curOneNum 当前字符串中1的个数 91 */ 92 public static void division(int curIndex, int curStrLen, int curOneNum) { 93 // 出口 94 if (curIndex == str.length()) { 95 if (curStrLen == L && curOneNum == N) { 96 count++; 97 } 98 return; 99 } 100 if (curStrLen > L) { 101 return; 102 } 103 if (curOneNum > N) { 104 return; 105 } 106 107 // 第一个开始字符是1的话,那么就从这个1开始,向后依次遍历 108 if (str.charAt(curIndex) == '1') { 109 for (int i = curIndex; i < str.length(); i++) { 110 // 如果是“1xxxx1”则不用判断,一定没有这种情况 111 /* 112 根据题意:能压缩的只有三种情况 113 1、111110 114 2、011110 115 3、011111 116 */ 117 118 // 开头和结尾都是1那么就直接跳过这种情况 119 if (i + 1 < str.length() && str.charAt(i + 1) == '1') { 120 continue; 121 } 122 int n = getD(str.substring(curIndex, i + 1)); 123 // 注意 “11” 的原字符串可能是 “111” 或者 “11” 所以n = 3时要多分一种情况 124 /* 125 如果长度为3,原串可能是111 126 但如果原串是11,就不需要转换他 127 所以我们不清楚结果中的11是经过转换的,还是说原来就是这样的 128 所以分两种情况: 129 */ 130 if (n == 3) { 131 // 原串为“11” 132 division(i + 1, curStrLen + 2, curOneNum + 2); 133 } 134 division(i + 1, curStrLen + n, curOneNum + n); 135 } 136 } else { 137 // 如果第一个字符不是1,而是零,则直接指针向后移一位,当前字符串的长度+1 138 division(curIndex + 1, curStrLen + 1, curOneNum); 139 } 140 } 141 142 /** 143 * 返回对应二进制数的十进制数 144 * @param binStr 145 * @return 146 */ 147 public static int getD(String binStr) { 148 int total = 0; 149 for (int i = 0; i < binStr.length(); i++) { 150 if (binStr.charAt(i) == '1') { 151 total += (long) Math.pow(2, (binStr.length() - 1 - i)); 152 } 153 } 154 return total; 155 } 156 }