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

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的考验。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Leetcode每日一题】 递归 - Pow(x, n)(难度⭐⭐)(40)

1. 题目解析 题目链接&#xff1a;50. Pow(x, n) 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 在这个算法中&#xff0c;递归函数的任务是求出 x 的 n 次方。那么&#xff0c;这个函数是怎么工作的呢&#xff1f;它…

基于springboot的大学生租房平台系统

技术&#xff1a;springbootmysqlvue 一、系统背景 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对大学生租房信息管理混乱…

RocketMQ学习笔记四(黑马)项目

课程地址&#xff1a; 1.Rocket第二章内容介绍_哔哩哔哩_bilibili &#xff08;视频35~88&#xff0c;搭建了一个电商项目&#xff0c;8h&#xff09; 待学&#xff0c;待完善。 项目安装&#xff08;打包&#xff09;的命令&#xff1a;&#xff08;注意-D后有空格&#xff09…

数据挖掘之关联规则

“啤酒和尿布的荣誉” 概念 项 item&#xff1a;单个的事物个体 &#xff0c;I{i1,i2…im}是所有项的集合&#xff0c;|I|m是项的总数项集&#xff08;item set)/模式&#xff08;pattern)&#xff1a;项的集合&#xff0c;包含k个项的项集称为k-项集数据集(data set)/数据库…

NCV8664CST50T3G稳压器芯片中文资料规格书PDF数据手册引脚图图片价格参数

产品概述&#xff1a; NCV8664C 是一款精确 3.3 V 和 5.0 V 固定输出&#xff0c;低漏集成式电压稳压器&#xff0c;输出电流能力为 150 mA。对轻型负载电流消耗的精心管理&#xff0c;并结合低漏工艺&#xff0c;实现了 22 A 的典型静态电流。输出电压精度在 2.0&#xff05;…

TikTok运营要用什么代理IP?如何分辨?

对于运营TikTok的从业者来说&#xff0c;IP的重要性自然不言而喻。 在其他条件都正常的情况下&#xff0c;拥有一个稳定&#xff0c;纯净的IP&#xff0c;你的视频起始播放量很可能比别人高出不少&#xff0c;而劣质的IP轻则会限流&#xff0c;重则会封号。那么&#xff0c;如何…

Linux 文件系统:重定向、缓冲区

目录 一、重定向 1、输出重定向 2、输入重定向 3、追加重定向 4、dup2 系统调用 二、理性理解Linux系统下“一切皆文件” 了解硬件接口 三、缓冲区 1、为什么要有缓冲区? 2、刷新策略 3、缓冲模式改变导致发生写时拷贝 未创建子进程时 创建子进程时 使用fflush…

贾志杰“大前端”系列著作出版发行

杰哥著作《VueSpringBoot前后端分离开发实战》2021年出版以来&#xff0c;累计发行2.6万册&#xff0c;受到广大读者热捧。后应读者要求&#xff0c;受出版社再次邀请&#xff0c;“大前端”系列之《剑指大前端全栈工程师》、《前端三剑客》由清华大学出版社陆续出版发行。系列…

Django日志(二)

一、Handler Handler决定如何处理logger中的每条消息。它表示一个特定的日志行为,例如 将消息写入屏幕、文件或网络Socket handler对应的是个字典,每一个键都是一个handler的名字,每个值又一个字典,描述了如何配置对应的handler实例 2.1、内置Handler class(必需):处理…

STM32最小核心板使用HAL库ADC读取MCU温度(使用DMA通道)

STM32自带CPU的温度数据&#xff0c;需要使用ADC去读取。因此在MX创建项目时如图配置&#xff1a; 模块初始化代码如下&#xff1a; void MX_ADC1_Init(void) {/* USER CODE BEGIN ADC1_Init 0 *//* USER CODE END ADC1_Init 0 */ADC_ChannelConfTypeDef sConfig {0};/* USER…

敢为天下先!深圳市全力推动鸿蒙生态发展……程序员

3月19日&#xff0c;鸿蒙生态创新中心揭幕仪式在深圳正式举行。鸿蒙生态创新中心的建立是为构建先进完整、自主研发的鸿蒙生态体系&#xff0c;将深圳打造为鸿蒙生态策源地、集聚区的具体举措&#xff0c;也是推动我国关键核心技术高水平自立自强、数字经济高质量发展、保障国家…

开源的OCR工具基本使用:PaddleOCR/Tesseract/CnOCR

前言 因项目需要&#xff0c;调研了一下目前市面上一些开源的OCR工具&#xff0c;支持本地部署&#xff0c;非调用API&#xff0c;主要有PaddleOCR/CnOCR/chinese_lite OCR/EasyOCR/Tesseract/chineseocr/mmocr这几款产品。 本文主要尝试了EasyOCR/CnOCR/Tesseract/PaddleOCR这…

基于Springboot+Vue的在线考试系统

项目介绍 这是一个在线考试系统&#xff0c;使用Maven进行项目管理&#xff0c;基于springbootmybatis框架开发的项目&#xff0c;mysql底层数据库&#xff0c;前端采用VueElementPlus&#xff0c;作为初学springbootvue前后端分离架构的同学是一个很不错的项目&#xff0c;如…

软件工程-第5章 结构化设计

5.1 总体设计的目标及其表示方法 5.2 总体设计 变换设计基本步骤&#xff1a; 第1步&#xff1a;设计准备--复审并精华系统模型&#xff1b; 第2步&#xff1a;确定输入、变换、输出这三部分之间的边界&#xff1b; 第3步&#xff1a;第一级分解--系统模块结构图顶层和第一层…

大模型来了,你的“存力”攒够了吗?

作者 | 曾响铃 文 | 响铃说 提到AI、大模型&#xff0c;很多人脑海里最先想到的是算力、算法、数据这“三驾马车”。 而要论谁最重要&#xff0c;恐怕多数人都会觉得是算力。 毕竟&#xff0c;“算力紧缺”的气氛常常被渲染起来。 然而&#xff0c;随着大模型进一步演进&a…

MySQL 字段定义时的属性设置

开发的时候第一步就是建表&#xff0c;在创建表的时候&#xff0c;我们需要定义表的字段&#xff0c;每个字段都有一些属性&#xff0c;比如说是否为空&#xff0c;是否允许有默认值&#xff0c;是不是逐渐等。 这些约束字段的属性&#xff0c;可以让字段的值更符合我们的预期&…

什么是代理IP?TikTok运营需要知道的IP知识

对于运营TikTok的从业者来说&#xff0c;IP的重要性自然不言而喻。 在其他条件都正常的情况下&#xff0c;拥有一个稳定&#xff0c;纯净的IP&#xff0c;你的视频起始播放量很可能比别人高出不少&#xff0c;而劣质的IP轻则会限流&#xff0c;重则会封号。那么&#xff0c;如何…

ThreaTrace复现记录

1. 环境配置 服务器环境 需要10.2的cuda版本 conda环境 包的版本&#xff1a; python 3.6.13 pytorch 1.9.1 torch-cluster 1.5.9 torch-scatter 2.0.9 torch-sparse 0.6.12 torch-spline-conv 1.2.1 torch-geometric 1.4.3 环境bug 这里环境搭建好以后&#xff0c;就可以正…

有哪些工具可以替代Gitbook?这篇文章告诉你

你是否曾经在搜索在线文档创建和共享工具时&#xff0c;遇到了Gitbook? Gitbook 是一个相当出色的工具&#xff0c;具有强大的编辑和发布功能&#xff0c;但也有其不足之处&#xff0c;如使用起来有一定的技术要求&#xff0c;入门门槛较高等。如果你正在寻找Gitbook的替代品&…

harmonyOS简介及背景

harmonyOS的场景模式18n: 1&#xff08;入口手机&#xff09;8&#xff08;电脑、VR、手环、iPad、智慧屏、&#xff09;–wifi—n(车载、智能家居等所有)harmonyOS不需要考虑软硬件的差异&#xff0c;是一个兼容N种的超级终端harmonyOS干了两件事&#xff1a; &#xff08;1&a…