zoukankan      html  css  js  c++  java
  • 《算法竞赛入门经典》 例题 44 信息编码 (Message Decoding,ACM,ICPC World Finals 1991,UVa 213)

    原题及翻译

    Some message encoding schemes require that an encoded message be sent in two parts.
    某些消息编码方案要求将编码的消息分为两部分发送。
    The first part, called the header, contains the characters of the message.
    第一部分称为标题,包含消息的字符。
    The second part contains a pattern that represents the message.
    第二部分包含表示消息的模式。
    You must write a program that can decode messages under such a scheme.
    您必须编写一个程序,在这种方案下可以解码消息。
    The heart of the encoding scheme for your program is a sequence of “key” strings of 0’s and 1’s as follows:
    程序编码方案的核心是一系列0和1的“键”字符串,如下所示:
    0, 00, 01, 10, 000, 001, 010, 011, 100, 101, 110, 0000, 0001, . . . , 1011, 1110, 00000, . . .
    The first key in the sequence is of length 1, the next 3 are of length 2, the next 7 of length 3, the next 15 of length 4, etc.
    序列中的第一个键为长度1,下一个3为长度2,下一个7为长度3,下一个15为长度4等。
    If two adjacent keys have the same length, the second can be obtained from the first by adding 1 (base 2).
    如果两个相邻键的长度相同,则可以通过添加1(基2)从第一个键获取第二个键。
    Notice that there are no keys in the sequence that consist only of 1’s.
    注意,序列中没有只包含1的键。
    The keys are mapped to the characters in the header in order.
    键按顺序映射到头中的字符。
    That is, the first key (0) is mapped to the first character in the header, the second key (00) to the second character in the header, the kth key is mapped to the kth character in the header.
    也就是说,第一个键(0)映射到头中的第一个字符,第二个键(00)映射到头中的第二个字符,第k个键映射到头中的第k个字符。
    For example, suppose the header is:
    例如,假设头是:
    AB#TANCnrtXc
    Then 0 is mapped to A, 00 to B, 01 to #, 10 to T, 000 to A, …, 110 to X, and 0000 to c.
    然后0映射到A,00到B,01到#,10到T,000到A,…,110到X,0000到c。
    The encoded message contains only 0’s and 1’s and possibly carriage returns, which are to be ignored.
    编码的消息只包含0和1,并且可能包含回车,这将被忽略。
    The message is divided into segments.
    消息分为段。
    The first 3 digits of a segment give the binary representation of the length of the keys in the segment.
    段的前3位数字给出段中键的长度的二进制表示。
    For example, if the first 3 digits are 010, then the remainder of the segment consists of keys of length 2 (00, 01, or 10).
    例如,如果前3位是010,则段的其余部分由长度为2(00、01或10)的键组成。
    The end of the segment is a string of 1’s which is the same length as the length of the keys in the segment.
    段的末尾是一个1的字符串,其长度与段中键的长度相同。
    So a segment of keys of length 2 is terminated by 11.
    因此,长度为2的键段以11结尾。
    The entire encoded message is terminated by 000 (which would signify a segment in which the keys have length 0).
    整个编码的消息以000结尾(这表示密钥长度为0的段)。
    The message is decoded by translating the keys in the segments one-at-a-time into the header characters to which they have been mapped.
    通过将段中的键一次转换为它们映射到的头字符,可以对消息进行解码。

    Input

    输入
    The input file contains several data sets.
    输入文件包含多个数据集。
    Each data set consists of a header, which is on a single line by itself, and a message, which may extend over several lines.
    每个数据集包括一个单独在一行上的头和一条可能扩展到多行上的消息。
    The length of the header is limited only by the fact that key strings have a maximum length of 7 (111 in binary).
    头的长度仅受以下事实的限制:键字符串的最大长度为7(二进制为111)。
    If there are multiple copies of a character in a header, then several keys will map to that character.
    如果一个标题中有一个字符的多个副本,那么几个键将映射到该字符。
    The encoded message contains only 0’s and 1’s, and it is a legitimate encoding according to the described scheme.
    编码的消息只包含0和1,根据所描述的方案,它是合法的编码。
    That is, the message segments begin with the 3-digit length sequence and end with the appropriate sequence of 1’s.
    也就是说,消息段以3位长度序列开始,以1的适当序列结束。
    The keys in any given segment are all of the same length, and they all correspond to characters in the header.
    任何给定段中的键都具有相同的长度,并且它们都对应于头中的字符。
    The message is terminated by 000.
    消息被000终止。
    Carriage returns may appear anywhere within the message part.
    回车可以出现在消息部分的任何位置。
    They are not to be considered as part of the message.
    它们不应被视为消息的一部分。

    Output

    输出
    For each data set, your program must write its decoded message on a separate line.
    对于每个数据集,程序必须将其解码后的消息写在单独的行上。
    There should not be blank lines between messages.
    消息之间不应该有空行。
    Sample input
    样例输入
    TNM AEIOU
    0010101100011
    1010001001110110011
    11000
    $#**
    0100000101101100011100101000
    Sample output
    样例输出
    TAN ME
    ##*$

    思路分析

    代码分析

    1.首先是读取编码并将编码存储的函数。
    在使用全局变量中的数组时,应该先把数组清零:

     memset(code,0,sizeof(code));
    

    由于编码头自身占一行,所以可以用readchar()读取第一个字符,而用普通的getchar()读取剩下的字符,知道换行符为止:

     code [1][0]=readchar();
    

    而readchar()是这样定义的:

    int readchar()
    {//跨行读字符。
     for(;;)
     {
      int ch=getchar();
      if(ch!='\n'&&ch!='\r') return ch;  //一直读到非换行符为止。
      //也就是说,当读到不是换行符的时候,返回ch的内容。
     }
    }
    

    可以用codes[len][value]这个二维数组来表示一个编码,其中len是编码长度,value是编码对应的十进制值。二进制中,8位最大整数就是8个1,即28-1,用移位运算符表示就是(1<<8)-1。

    int readcodes()
    {//读取编码。
     memset(code,0,sizeof(code));
     code [1][0]=readchar();
     //直接调到下一行开始读取。如果输入已经结束,会读到EOF。
     for(int len=2;len<=7;len++)
     {
      for(int i=0;i<(1<<len)-1;i++)
      {
       int ch=getchar();
       if(ch==EOF) return 0;
       if(ch=='\n'||ch=='\r') return 1;
       code[len][i]=ch;
      }
     }
     return 1;
    }
    

    2.然后是将二进制转化为十进制的函数,这里采用的二进制转十进制是一次添加一位的运算方式,每次乘以2,比如说:1012=((1*2)+0)*2+1=5。

    int readint(int c)
    {//读取c位二进制字符(即0和1),并转化为十进制整数。
     int v=0;
     while(c--) v=v*2+readchar()-'0';
     return v;
    }
    

    3.最后就是主函数了,读入数据,调用函数做处理,输出结果。
    用一个无限循环连续处理数据:

      for(;;)
    

    要读入开头的三个数字代表小节中每个编码的长度:

       int len=readint(3);
    

    然后再来一个无限循环处理代表编码长度后边、结束标志前边的数字:

       for(;;)
       {
        int v=readint(len);
        //v是len十进制位的二进制数转化为十进制数后的数
        if(v==(1<<len)-1) break;
        //当小节编码结尾标志11…1出现时,跳出当前循环
        putchar(code[len][v]);
       }
      }
    
    

    把这些组合起来就是主函数:

    int main()
    {
     while(readcodes())
     {//无法读取更多编码头时时退出。
      for(;;)
      {
       int len=readint(3);
       if(len==0) break;
       //即结尾标志:000
       for(;;)
       {
        int v=readint(len);
        if(v==(1<<len)-1) break;
        putchar(code[len][v]);
       }
      }
      putchar('\n');
     }
     return 0;
    }
    

    完整代码

    #include <stdio.h>
    #include <string.h>
    int code[8][1<<8];		//1<<8表示2的八次方
    int readchar()
    {//跨行读字符。
     for(;;)
     {
      int ch=getchar();
      if(ch!='\n'&&ch!='\r') return ch;  //一直读到非换行符为止。
      //也就是说,当读到不是换行符的时候,返回ch的内容。
     }
    }
    int readint(int c)
    {//读取c位二进制字符(即0和1),并转化为十进制整数。
     int v=0;
     while(c--) v=v*2+readchar()-'0';
     return v;
    }
    int readcodes()
    {//读取编码。
     memset(code,0,sizeof(code));
     code [1][0]=readchar();
     //直接调到下一行开始读取。如果输入已经结束,会读到EOF。
     for(int len=2;len<=7;len++)
     {
      for(int i=0;i<(1<<len)-1;i++)
      {
       int ch=getchar();
       if(ch==EOF) return 0;
       if(ch=='\n'||ch=='\r') return 1;
       code[len][i]=ch;
      }
     }
     return 1;
    }
    int main()
    {
     while(readcodes())
     {//无法读取更多编码头时时退出。
      for(;;)
      {
       int len=readint(3);
       if(len==0) break;
       for(;;)
       {
        int v=readint(len);
        if(v==(1<<len)-1) break;
        putchar(code[len][v]);
       }
      }
      putchar('\n');
     }
     return 0;
    }
    

    结语

    大道至简,最简单的代码也许难以理解,但这正是算法的美妙之处。

    每天磕一道ACM 大年初一打卡

  • 相关阅读:
    类的嵌套
    一种设计模式--单例模式
    python中的类(二)
    Python中的类(一)
    基于session和cookie的登录验证(CBV模式)
    Django中的CBV和FBV
    python3 装饰器
    cookie和session
    基于cookie和session的登录验证
    python3 安装win32api
  • 原文地址:https://www.cnblogs.com/AlexKing007/p/12339568.html
Copyright © 2011-2022 走看看