电子科技大学链时代工作室招新题C语言部分---题号H

news2024/9/17 9:22:54

1. 题目

最有操作的一道题,有利于对贪心算法有个初步了解。

  这道题的开篇向我们介绍了一个叫汉明距离的概念。

汉明距离指的就是两个相同长度的字符串的不同字符的个数。

例如,abc和acd,b与c不同,c与d不同,所以这两个字符串的汉明距离就是2。

 这道题就要求我们找到一个字符串,使得该字符串与已知的两个字符串的汉明距离相同,并且该字符串在字典序上要尽可能地小。(字典序上最小,就是在符合条件的字符串中,用strcmp比较出来的最小的那个)。

题目言简意赅,但却是最考验思路的一道题。

输入

第一行输入一个整数T(1 <= T <= 100),表示接下来要输入几个案例。

接下来输入T个测试案例,对每个案例,输入两行长度相同的字符串(单个字符串长度不超过10^{4},字符串总长度不超过10^{6})。

输出

输出T行,每行为其对应案例的所求字符串。


2. 第一版解法

前面说过,这道题十分考验思路,我们第一版时,我们没有找到很好的办法,解法比较暴力,思路也比较混乱。

2.1 思路

1. 我们要比较目标字符串与两个原字符串的汉明距离,那我们势必要先生成一个目标字符串,然后再根据汉明距离的差异来调整目标字符串。

2. 既然要目标字符串在字典序上最小,那我们一开始生成的目标字符串就尽可能小,并且只在必要时调整,这样才能保证得到我们想要的结果。

3. 所以,我们决定将目标字符串初始化为每个元素都是'a',并从后往前调整(越靠前的字符权重越高,所以优先调整靠后的字符)。

4. 初始时,两个原字符串与目标字符串的汉明距离分别就是两个字符串中不等于'a'的字符的数量,然后根据不同的情况,我们对字符做不同的调整。

2.2 代码

#include <stdio.h>
#include <string.h>

int main()
{
    int T = 0;
    scanf("%d", &T);
    char arr1[10001] = {0};
    char arr2[10001] = {0};
    char ret[101][10001] = {0};
    for(int k = 0; k < T; k++)
    {
        char* tmp = &ret[k][0];
        scanf("%s", arr1);
        scanf("%s", arr2);
        int len = strlen(arr1);
        int hm1 = len, hm2 = len;//记录汉明数
        tmp[len] = '\0';
        for(int i = 0; i < len; i++)
        {
            tmp[i] = 'a';
            if(arr1[i] == 'a')
            hm1--;
            if(arr2[i] == 'a')
            hm2--;
        }
        int op = len - 1;
        while(hm1 != hm2)
        {
            if(arr1[op] == arr2[op])//两个相同,改变无意义且会增大字符
            {
                op--;
            }
            else if(hm1 - hm2 >= 2&&'a' == arr2[op])//汉明数相差超过二
            {
                tmp[op] = arr1[op];
                hm1--;
                hm2++;
                op--;
            }
            else if(hm2 - hm1 >= 2&&'a' == arr1[op])
            {
                tmp[op] = arr2[op];
                hm2--;
                hm1++;
                op--;
            }
            else if(op >= 1&&hm1 - hm2 == 2&&'a' == arr2[op - 1]&&arr1[op - 1] == 'b')//下一次能改两,且刚好差两
            {
                tmp[op - 1] = arr1[op - 1];
                hm1--;
                hm2++;
                op--;
            }
            else if(op >= 1&&hm2 - hm1 == 2&&'a' == arr1[op - 1]&&arr2[op - 1] == 'b')
            {
                tmp[op - 1] = arr2[op - 1];
                hm2--;
                hm1++;
                op--;
            }
            else if(hm1 > hm2&&'a' != arr1[op]&&'a' != arr2[op])
            {
                tmp[op] = arr1[op];
                hm1--;
                op--;
            }
            else if(hm1 < hm2&&'a' != arr1[op]&&'a' != arr2[op])
            {
                tmp[op] = arr2[op];
                hm2--;
                op--;
            }
            else if(hm1 - hm2 == 1&&'a' == arr2[op])
            {
                for(char j = 'a'; j <= 'z'; j++)
                {
                    if(arr1[op] != j&&arr2[op] != j)
                    {
                        tmp[op] = j;
                        break;
                    }
                }
                hm2++;
                op--;
            }
            else if(hm2 - hm1 == 1&&'a' == arr1[op])
            {
                for(char j = 'a'; j <= 'z'; j++)
                {
                    if(arr1[op] != j&&arr2[op] != j)
                    {
                        tmp[op] = j;
                        break;
                    }
                }
                hm1++;
                op--;
            }
            else
            {
                op--;
            }
        }
    }
    for(int i = 0; i < T; i++)
    {
        printf("Case %d: %s\n", i + 1, ret[i]);
    }
    return 0;
}

2.3 总结

这一版中,我们考虑了许多种情况,可以看到if和else语句写了一长串,以告诉程序在遇到不同情况时该怎么做。

感兴趣的小伙伴可以仔细看看第4和5个分支,这种情况是最难想到的。比如输入aab和bbc时,正确答案应该是aba,而没有这两个分支时会输出acc。

虽然这段代码看起来天衣无缝,万无一失,能讨论的情况都考虑了(我至今都想不通到底是什么测试用例会导致其失败)。但是在oj上跑时,始终会提示“wrong answer on test 2!”。

还记得我们总结的经验教训吗,当你的代码开始用if语句处理特殊输入时,就要开始考虑改改了。

用if和else语句来告诉计算机该怎么处理不同的情况,这样的算法是远比不上一个通用的算法的。

3. 最终版解法

这一版解法是我在走投无路的情况下向大佬求教,得到耐心指点之后才搞定的。

3.1 思路

这一版的思路将我们之前的做法全盘推翻了,这一次我们采用从前向后操作的顺序,并且不是调整出目标字符串,而是直接生成出目标字符串。

在写出第一版之前,我也考虑过从前往后生成目标字符串的写法。但是,这么做会有两个问题:

1. 目标字符串逐步生成,那么我在生成过程中如何知道当前位置该放什么,才能保证之后有办法使得目标字符串与两个原字符串的汉明距离相同(之后称这个目标为“汉明距离差值为0”)?

2. 如何确保我生成的字符串是字典序最小的?

为了解决这两个问题,我们需要深入地去挖掘一些隐藏的数学关系:

导致目标字符串与两个原字符串的汉明距离不同的,只能是两个原字符串字符不同的位置。也就是说,在目标字符串生成到某个位置时,它与两个原字符串的汉明距离的差值一定小于未生成部分,两个原字符串的汉明距离。我们只需要确保这一点成立,就可以将尽可能小的字符放在该位置。

 好像这段话不长,但我就是看不懂?

没关系,好好理解一下,很快就会有一种恍然大悟的感觉。

那么,为什么要从前往后操作呢?因为随着我操作的进行,需要满足的条件会越来越苛刻。前面的字符更高贵,苦了后面也不能苦前面。

这个思路的妙处在于,它要求要满足的条件是一个必要条件,将条件限制到最苛刻的情况(必要条件不满足,结果一定不成立),我再考虑放置较大的字符,这样就可以确保我得到的是最小字符串。这也就是贪心算法的基本思想。

而且,随着我操作的进行,两个原字符串的汉明距离会逐渐减小,那么目标字符串与两个原字符串的汉明距离之差也会逐渐减小,直到最后归于零。

3.2 代码

#include <stdio.h>
#include <string.h>

int HanMingDistance(char* str1, char* str2, int len)
{
    int distance = 0;
    for(int i = 0; i < len; i++)
    {
        if(str1[i] != str2[i])
        distance++;
    }
    return distance;
}

int main()
{
    int T = 0;
    scanf("%d", &T);
    char arr1[10001] = {0};
    char arr2[10001] = {0};
    char ret[101][10001] = {0};
    for(int k = 0; k < T; k++)
    {
        char* tmp = &ret[k][0];
        scanf("%s", arr1);
        scanf("%s", arr2);
        int len = strlen(arr1);
        tmp[len] = '\0';
        int dice = 0;//输入的两个字符串当前位置起的汉明距离
        int hm1 = 0, hm2 = 0;
        for(int i = 0; i < len; i++)
        {
            if(arr1[i] == arr2[i])
            {
                tmp[i] = 'a';
                continue;
            }
            dice = HanMingDistance(arr1+i, arr2+i, len-i);
            int tem1 = 0, tem2 = 0;
            for(char j = 'a'; j <= 'z'; j++)
            {
                if(arr1[i] != j)
                tem1 = 1;
                else
                tem1 = 0;
                if(arr2[i] != j)
                tem2 = 1;
                else
                tem2 = 0;
                if(hm1 + tem1 - hm2 - tem2 < dice&&hm2 + tem2 - hm1 -tem1 < dice)
                {
                    hm1 = hm1 + tem1;
                    hm2 = hm2 + tem2;
                    tmp[i] = j;
                    break;
                }
            }
        }
    }
    for(int i = 0; i < T; i++)
    {
        printf("Case %d: %s\n", i + 1, ret[i]);
    }
    return 0;
}

3.3 总结

这一版仅65行,差不多只有第一版的一半,但是却能顺利通过oj的考验。

算法,真是一个神奇的东西,通过这道题,我们也能看见研究算法的重要性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1535833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux C | 多线程编程】线程的创建、线程ID、线程属性

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-03-22 0…

LiDAR 点云数据综合指南

LiDAR 是光探测和测距的缩写,它彻底改变了各个领域的数据采集。它生成密集 3D 点云的能力为我们的世界提供了无与伦比的洞察力。 但如此丰富的信息也带来了复杂性,特别是在理解不同类型的激光雷达点云数据时。本指南旨在成为您的一站式资源,阐明其中的细微差别,并使您能够…

python基础——对序列的通用操作【+和*、in、切片操作、separator.join(iterable)】

&#x1f4dd;前言&#xff1a; 我们已经学习了python数据容器中的列表&#xff0c;元组以及字符串。而他们都属于序列 &#xff08;序列是指&#xff1a;内容连续&#xff0c;有序&#xff0c;可以用下标索引访问的数据容器&#xff09; 在之前已经介绍了不少操作方法&#xf…

工作需求,Vue实现登录

加油&#xff0c;新时代打工人&#xff01; vue 2.x Element UI <template><div class"body" :style"{background-image: url(${require(/assets/images/login.png)})}"><el-form :rules"rules" ref"loginForm" :mode…

线性表:关于链表(主要以单链表为例)的相关理解和应用

多清澈这天空 晴雨相拥 同心逐梦&#xff01; 坚守我信心 一路出众&#xff01;&#xff01; 首先&#xff0c;按照惯例&#xff0c;欢迎大家边听歌边观看本博客 ▶ 紫荆花盛开 (163.com)&#xff08;建议复制链接&#xff0c;浏览器打开&#xff0c;csdn打开太慢了&#x…

mysql数据类型和常用函数

目录 1.整型 1.1参数signed和unsigned 1.2参数zerofill 1.3参数auto_increment 2.数字类型 2.1floor()向下取整 2.2随机函数rand() 2.3重复函数repeat() 3.字符串类型 3.1length()查看字节长度&#xff0c;char_length()查看字符长度 3.2字符集 3.2.1查看默认字符…

工程信号的去噪和(分类、回归和时序)预测

&#x1f680;【信号去噪及预测论文代码指导】&#x1f680; 还为小论文没有思路烦恼么&#xff1f;本人专注于最前沿的信号处理与预测技术——基于信号模态分解的去噪算法和深度学习的信号&#xff08;回归、时序和分类&#xff09;预测算法&#xff0c;致力于为您提供最精确、…

MySql实战--深入浅出索引(下)

在开始这篇文章之前&#xff0c;我们先来看一下这个问题&#xff1a; 在下面这个表T中&#xff0c;如果我执行 select * from T where k between 3 and 5&#xff0c;需要执行几次树的搜索操作&#xff0c;会扫描多少行&#xff1f; 下面是这个表的初始化语句。 图1 InnoDB的索…

第 6 章 ROS-xacro练习(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 6.4.3 Xacro_完整使用流程示例 需求描述: 使用 Xacro 优化 URDF 版的小车底盘模型实现 结果演示: 1.编写 X…

ChatGPT已经掌控了全局:不仅写论文的在用ChatGPT,同行评审也在用ChatGPT!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

分库分表场景下多维查询解决方案(用户+商户)

在采用分库分表设计时&#xff0c;通过一个PartitionKey根据散列策略将数据分散到不同的库表中&#xff0c;从而有效降低海量数据下C端访问数据库的压力。这种方式可以缓解单一数据库的压力&#xff0c;提升了吞吐量&#xff0c;但同时也带来了新的问题。对于B端商户而言&#…

【Python爬虫】网络爬虫:信息获取与合规应用

这里写目录标题 前言网络爬虫的工作原理网络爬虫的应用领域网络爬虫的技术挑战网络爬虫的伦理问题结语福利 前言 网络爬虫&#xff0c;又称网络爬虫、网络蜘蛛、网络机器人等&#xff0c;是一种按照一定的规则自动地获取万维网信息的程序或者脚本。它可以根据一定的策略自动地浏…

linux查看usb是3.0还是2.0

1 作为device cat /sys/devices/platform/10320000.usb30drd/10320000.dwc3/udc/10320000.dwc3/current_speed 如下 high-speed usb2.0 super-speed usb3.0 2 作为host linux下使用以下命令查看 &#xff0c;如果显示 速率为5G, 则为USB 3.0&#xff0c; USB2.0通常显示速率…

Day17|二叉树part04:110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和、543: 二叉树的直径、124: 二叉树的最大路径和

之前的blog链接&#xff1a;https://blog.csdn.net/weixin_43303286/article/details/131982632?spm1001.2014.3001.5501 110.平衡二叉树 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。思路&#xff…

Matlab之已知2点绘制长度可定义的射线

目的&#xff1a;在笛卡尔坐标系中&#xff0c;已知两个点的位置&#xff0c;绘制过这两点的射线。同时射线的长度可以自定义。 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;射线的起点&#xff1b; PointB&#xff1a;射线过的零一点&#xff1b; Length&…

AI PPT生成工具 V1.0.0

AI PPT是一款高效快速的PPT生成工具&#xff0c;能够一键生成符合相关主题的PPT文件&#xff0c;大大提高工作效率。生成的PPT内容专业、细致、实用。 软件特点 免费无广告&#xff0c;简单易用&#xff0c;快速高效&#xff0c;提高工作效率 一键生成相关主题的标题、大纲、…

【链表】Leetcode 138. 随机链表的复制【中等】

随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点…

Linux - 应用层HTTPS、传输层TCP/IP模型中典型协议解析

目录 应用层&#xff1a;自定制协议实例 HTTP协议首行头部空行正文http服务器的搭建 HTTPS协议 传输层UDP协议TCP协议 应用层&#xff1a; 应用层负责应用程序之间的沟通—程序员自己定义数据的组织格式 应用层协议&#xff1a;如何将多个数据对象组织成为一个二进制数据串进行…

代码签名证书被吊销的原因及其后果是什么?

代码签名证书是确保软件代码完整性和可信度的关键工具&#xff0c;然而&#xff0c;在某些情况下&#xff0c;此类证书可能会被撤销。这意味着证书颁发机构&#xff08;CA&#xff09;不再认可该证书的有效性&#xff0c;并宣布其失效。本文将解析导致代码签名证书撤销的原因、…

Bytebase 2.14.1 - 分支 (Branching) 功能支持 Oracle

&#x1f680; 新功能 分支 (Branching) 功能支持 Oracle。为 SQL 编辑器添加了项目选择器。 新增 SQL 审核规范&#xff1a; 禁止混合 DDL、DML 语句。禁止对同一张表进行不同类型的 DML 变更 (UPDATE,INSERT,DELETE)。 &#x1f514; 重大变更 工作空间设置中的「数据访问…