20. 前缀码判定

1 描述

前缀码:任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀。

请编写一个程序,判断输入的n个由10组成的编码是否为前缀码。如果这n个编码是前缀码,则输出"YES”;否则输出第一个与前面编码发生矛盾的编码。

输入:
1行为n(表示下面有n行编码)
2n+1行为n个由01组成的编码

输出:判断结果

例如,如果输入:

5

00

01

10

110

111

每一个字符均不是其他字符编码的前缀,所以,输出:YES

再如,如果输入:

5

00

01

10

110

11

编码11与前面的编码110的前缀,所以,输出:11

 测试输入 期待的输出 时间限制 内存限制 额外进程
测试用例 1以文本方式显示
  1. 5↵
  2. 00↵
  3. 01↵
  4. 10↵
  5. 110↵
  6. 111↵
以文本方式显示
  1. YES↵
1秒64M0
测试用例 2以文本方式显示
  1. 5↵
  2. 00↵
  3. 01↵
  4. 10↵
  5. 110↵
  6. 11↵
以文本方式显示
  1. 11↵
1秒64M0
测试用例 3以文本方式显示
  1. 5↵
  2. 00↵
  3. 01↵
  4. 10↵
  5. 11↵
  6. 111↵
以文本方式显示
  1. 111↵
1秒64M0
测试用例 4以文本方式显示
  1. 5↵
  2. 111↵
  3. 110↵
  4. 10↵
  5. 01↵
  6. 00↵
以文本方式显示
  1. YES↵
1秒64M0
测试用例 5以文本方式显示
  1. 8↵
  2. 00↵
  3. 010↵
  4. 0110↵
  5. 0111↵
  6. 10↵
  7. 110↵
  8. 1110↵
  9. 1111↵
以文本方式显示
  1. YES↵
1秒64M0
测试用例 6以文本方式显示
  1. 8↵
  2. 00↵
  3. 010↵
  4. 0110↵
  5. 0111↵
  6. 10↵
  7. 11↵
  8. 1110↵
  9. 111↵
以文本方式显示
  1. 1110↵
1秒64M0

2 解题

  • 分析:建立一棵二叉树来处理,如果遇到0就处理左边,遇到1就处理右边
  • 例如:
    在这里插入图片描述
    根据用户的输入顺序来存储,先出现的编码先存到数组的前面位置,但是不影响唯一性,
    比如上图,如果下一个编码是010,那么就要从数组的第5位开始存了,下标5这个信息存储在数组第一个节点的leftpoint中,这样就串起来了
  • 代码:
/*
这颗树实际上就是一个一维数组,但是和完全二叉树存储方法不同,他并不能直接使用下标来遍历
准确的说这是一个存储在顺序表中的链表
节点的左右指针(就是int数字)指向下一个元素的下标
下一个元素的下标根据下一个数的取值来定,如果是0的话就是leftpoint指向的下标
如果是1的话就是rightpoint指向的下标
*/
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <cstring>
using namespace std;

struct node
{ //读到的数字是1就处理右节点,如果是0的话就处理左节点
    int flag = 0;
    int leftpoint = 0;
    int rightpoint = 0;
};
typedef struct node hufftree;

int main()
{
    int n;
    // 这样申请的动态数组并不会自动赋值为节点中的0,只是申请了空间
    // hufftree *ht;
    // ht = (hufftree*)malloc(sizeof(hufftree)*1000);
    hufftree ht[50000];
    char *str;

    int len;          //字符串(编码)的长度
    int treecout = 1; //这个树的长度
    int treeindex;    //正在处理的这个字符对应在树中的位置
    int i = 0;

    // freopen("file out.txt", "r", stdin);
    cin >> n;

    str = (char *)malloc(sizeof(char) * 100000);
    while (n--)
    {
        cin >> str;
        len = strlen(str);

        treeindex = 0;
        for (i = 0; i < len; i++)
        {
            if (ht[treeindex].flag == 1)
            { // flag为1,说明之前已经出现了一样的编码
                cout << str << endl;
                return 0; //只需要输出第一次重复的前缀就行,直接返回
            }
            if (i < len - 1)
            { //最后一个字符(结尾)需要单独处理,因为最后一位涉及flag,这里先讨论前面的字符
                if (str[i] == '0')
                { //如果是0,我们就来看左指针
                    if (ht[treeindex].leftpoint == 0)
                    { //如果还是初始值,说明没有出现过这个编码,直接建立就行了
                        ht[treeindex].leftpoint = treecout;
                        treeindex = treecout++; // cout的值始终比index的值大1
                        continue;
                    }
                    else
                    { //之前有相同的部分,直接按照leftpoint的指向得到下标
                        treeindex = ht[treeindex].leftpoint;
                        continue;
                    }
                }
                else
                {//如果是1,就看右指针
                    if (ht[treeindex].rightpoint == 0)
                    {
                        ht[treeindex].rightpoint = treecout;
                        treeindex = treecout++;
                        continue;
                    }
                    else
                    {
                        treeindex = ht[treeindex].rightpoint;
                        continue;
                    }
                }
            }
            else
            { //存最后一位
                if (str[i] == '0')
                { //是0,针对左指针操作
                    if (ht[treeindex].leftpoint == 0)
                    { //当前位的左指针没有使用,把他左边指针指向下一位,并且下一位的flag置为0
                        //记住:最后一位的相应指针是被使用了的,0:使用了leftpoint;1:使用了rightpoint
                        ht[treeindex].leftpoint = treecout;
                        ht[treecout].flag = 1;//是把下一位置的flag置为0
                        treecout++;
                        continue;
                    }
                    else
                    { //当前位的左指针被使用了,说明之前出现了相同的编码,而且程序运行到这说明是正在处理编码的最后一位,那么可以断定编码重复
                        cout << str << endl;
                        return 0;
                    }
                }
                else
                {//如果是1相同的道理
                    if (ht[treeindex].rightpoint == 0)
                    {
                        ht[treeindex].rightpoint = treecout;
                        ht[treecout].flag = 1;
                        treecout++;
                        continue;
                    }
                    else
                    {
                        cout << str << endl;
                        return 0;
                    }
                }
            }
        }
    }
    cout << "YES" << endl;
    return 0;
}
  • 最先写的时候我把最后一位的flag置为1,而不是把下一位的flag置为1,这样就会有争议,无法确定是已经结束的这个字符串的最后一个字符是0还是1,对之后的判断产生困扰,之后我又增加了一个flag,但是还是无法完全AC,直到改成现在这个程序才过

参考来源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值