环状串的字典序

【题目描述】

长度为n的环状串有n种表示法,分别为从某个位置开始顺时针得到。例如,图3-4的环状串有10种表示:

CGAGTCAGCT,GAGTCAGCTC,AGTCAGCTCG等。在这些表示法中,字典序最小的称为"最小表示"。

输入一个长度为n(n≤100)的环状DNA串(只包含A、C、G、T这4种字符)的一种表示法,你的任务是输出该环状串的最小表示。例如,CTCC的最小表示是CCCT,CGAGTCAGCT的最小表示为AGCTCGAGTC。

【样例输入】

2

CGAGTCAGCT

CTCC

【样例输出】

AGCTCGAGTC

CCCT

【题目来源】

刘汝佳《算法竞赛入门经典  第2版》 例题3-6 环状序列(Circular Sequence, ACM/ICPC Seoul 2004, UVa1584)

【解析】

一、原书代码:

1.字典序的概念

字典序,就是像英文字典中的顺序一样对字母排序。一般地,对于两个字符串,从第一个字符开始比较,当某一个位置的字符不同时,该位置字符较小的串,字典序较小(例如,abc比bcd小);如果其中一个字符串已经没有更多字符,但另一个字符串还没结束,则较短的字符串的字典序较小(例如,hi比history小)。

字典序的概念可以推广到任意序列,例如,序列1, 2, 4, 7比1, 2, 5小。

2.问题解决思路

解决问题的思路是怎么来的?把这个问题浓缩成一句话,就是按字典序排序环状串。这里面有两个关键词:字典序、环状串,它们一个是算法,一个是数据结构。

环状串的核心词是“串”,本质上还是字符串,只不过是环形的,因此比较的对象是字符串。把“字符串比较”这个问题简化到极致,最少要有两个字符串才能比较,所以先考虑怎么比较两个普通字符串。再由简至繁去思考:比较两个普通字符串→比较两个环状字符串→比较多个环状字符串。普通串到环状串的处理很简单,和钟由12点到1点的处理方法一样,对12取余即可。由两个字符串到多个字符串就更简单了,一个循环遍历搞定。

字典序的核心词是“序”,本质上也是排序问题,排序问题都需要遍历(一一比较),从这个思路出发,就很容易想到用遍历的方式进行字典序排序。只不过本题只是找出最小字典序,是排序的简化版,其算法本质上就像"求n个元素中的最小值"一样,只要拿其中1个与其它每个元素进行比较,一直取其中的最小值即可。不同的是,字典序比较的不再只是一个值,而是依次比较每一位(相同时继续往下比较,不同时字符较小的字典序较小)。可用变量ans表示目前为止,字典序最小串在输入串中的起始位置,然后不断更新ans。

代码如下:

#include<stdio.h>
#include<string.h>
#define maxn 105

//环状串s的表示法p是否比表示法q的字典序小
int less(const char* s, int p, int q) {
    int n = strlen(s);
    for(int i = 0; i < n; i++)
        if(s[(p+i)%n] != s[(q+i)%n])
            return s[(p+i)%n] < s[(q+i)%n];
    return 0; //相等
}
int main() {
    int T;
    char s[maxn];
    scanf("%d", &T);
    while(T--) {
        scanf("%s", s);
        int ans = 0;
        int n = strlen(s);
        for(int i = 1; i < n; i++)
            if(less(s, i, ans)) ans = i;
        for(int i = 0; i < n; i++)
            putchar(s[(i+ans)%n]);
        putchar('\n');
    }
    return 0;
}

二、老金代码

说实话,这道题着实把老金难住了,解题过程思路混乱,折腾了一小天才总算写出了代码。

老金也明白解决问题的关键是找到字典序最小的环状串在输入串中的起始位置。

但是老金在考虑算法时深深陷入人的思维不能自拔。如果让我们人去解决这个问题,都会想当然地首先去找到字符串中最小的字母,然后只需依次比较其后面的每一位就可以了。

拿输入样例为例,CGAGTCAGCT的比较过程:

最小位置

最小字母

后1位

后2位

3

A

G

T

7

A

G

C

CTCC的比较过程:

最小位置

最小字母

后1位

后2位

1

C

T

3

C

C

C

4

C

C

T

这种方法能够逐步缩小比较范围,最终找到只有1个最小字母时,就是最小的字典序了。

这其实是一种走捷径的思维。因为人的计算能力远低于电脑,所以会优先选择计算量少的方法,因而自然不会傻到逐一比较以每位字母开始的环状串的字典序。

但正是基于这个人走捷径的思维,才导致老金算法实现的复杂。

①找最小字母。让人找一个字符串中的所有最小字母,扫一眼就能找出,但让电脑找就不那么简单了。老金想到的方法是先拿最小的字符A与串中的每一位比较,如果找到相等的就说明是最小字母,如果没找到就再依次拿C、G、T继续找。

②存储首个最小字母下标。要存储最开始找到的所有最小字母的下标,这就需要用到数组min[]。

③依次判断首字母后面的字母。还得再一次用第①步找最小字母的方法,因为找的位置与①不同,还要单独写代码。

④实时去除非最小字典序的环状串。还有一点更麻烦的,每比较一次,都要从min[]数组中去掉不再属于最小字典序的下标。老金采取的方法是每次都对min[]数组的元素值和元素个数重新赋值。

可见,老金的算法的问题就是算法的种类多,要考虑的事情也就越大、自然需要写的代码量就大。相比之下,原书中的算法是通过遍历比较字典序,算法只有一种,自然代码量就小。

#include<stdio.h>
#include<string.h>
char dna[105];
int min[105]; //dna中最小字母的下标
int main(){
    char c[4]={'A', 'C', 'G', 'T'};
    int n;
    scanf("%d", &n);
    while(n--){
        int y=0, index;
        memset(dna, 0, sizeof(dna));
        scanf("%s", dna);
        int len = strlen(dna);
        //为min数组赋值
        int flag=1, x=0;
        for(int i=0; i<4 && flag; i++){
            for(int j=0; j<len; j++){
                if(c[i]==dna[j]) {
                    flag=0;
                    min[x++]=index=j;
                }
            }
        }
        //如果找到多个最小字母,依次判断其后面的最小字母的数量,直到其数量为1
        if(1!=x){
            for(int y=1; y<len; y++){
                int flag=1, cnt=0;
                for(int i=0; i<4 && flag; i++){
                    for(int j=0; j<x; j++){
                        if(c[i]==dna[(min[j]+y)%len]) {
                            flag=0;
                            index=min[j];
                            min[cnt]=min[j];
                            cnt++;
                        }
                    }
                    if(cnt) x=cnt;
                }
                if(1==cnt) break;
            }
        }
        for(int k=index; k<len+index; k++) printf("%c", dna[k%len]);
        printf("\n");
    }
    return 0;
}

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

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

相关文章

利用GaussDB的可观测性能力构建故障模型

D-SMART高斯专版已经开发了几个月了&#xff0c;目前主要技术问题都已经解决&#xff0c;也能够初步看到大概的面貌了。有朋友问我&#xff0c;GaussDB不已经有了TPOPS了&#xff0c;为什么你们还要开发D-SMART高斯专版呢&#xff1f; 实际上TPOPS和D-SMART虽然都可以用于Gaus…

区块链技术下的DApp与电商:融合创新,开启商业新纪元

区块链技术的蓬勃发展正引领着一种新型应用程序的崛起——去中心化应用程序&#xff08;DApp&#xff09;。DApp并非传统的中心化应用&#xff0c;它构建于去中心化网络之上&#xff0c;融合了智能合约与前端用户界面&#xff0c;为用户提供了全新的交互体验。智能合约&#xf…

01.Kafka简介与基本概念介绍

1 Kafka 简介 Kafka 是最初由 Linkedin公司开发&#xff0c;是一个分布式、支持分区(partition)的、多副本(replica)的&#xff0c;基于 Zookeeper 协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&#xff1a;比如基于 hadoop 的…

算法工程师——算法岗的分类及要求汇总

算法岗工程师 根据 Talent Seer 人才报告显示,全球 AI 从业者总人数约有 30 万,还是供不应求,其中 AI 技术专家(具有相关领域博士学位及 3 年以上工作经验的)约有 3.65 万。 简介 对于计算机专业的毕业生而言,算法岗基本上就是 「高薪」 的代名词。 在当今 IT 行业,算…

如何将本地项目上传到Github(SSH方式)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

训练营第三十七天动态规划(基础题part3)

训练营第三十七天动态规划&#xff08;基础题part3&#xff09; 343. 整数拆分 力扣题目链接 题目 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 …

一篇文章 学会Qt 样式表(qss)

QML 中风格和主题的设计可以通过配置文件选择现有几种中的一种&#xff0c;或者直接在控件定义时&#xff0c;指定其属性&#xff0c;如背景颜色或者字体大小。在QWidget框架中&#xff0c;则通过了一种叫做qss样式表的东西来进行描述&#xff0c;跟CSS逻辑上类似。 这个qss抽…

【Redis 开发】多级缓存,本地进程缓存Caffeine

多级缓存 多级缓存本地进程缓存CaffeineCaffeine三种缓存驱逐策略 多级缓存 Redis处理并发的能力是非常强大的&#xff0c;但是tomcat的支持并发的能力跟不上Redis的性能&#xff0c;导致整体性能的下降 Redis缓存失效时&#xff0c;会对数据库产生冲击&#xff0c;之间再无屏…

自动驾驶横向控制算法

本文内容来源是B站——忠厚老实的老王&#xff0c;侵删。 三个坐标系和一些有关的物理量 使用 frenet坐标系可以实现将车辆纵向控制和横向控制解耦&#xff0c;将其分开控制。使用右手系来进行学习。 一些有关物理量的基本概念&#xff1a; 运动学方程 建立微分方程 主要是弄…

软件测试之学习及复习面试路线汇总

对于很多想通过自学或面试复习软件测试的同学&#xff0c;痛点并不是学习动力&#xff0c;而是找不到清晰的学习思路。 熬夜3天&#xff0c;吐血整理了这份《软件测试学习路线》&#xff0c;全文接近6000字&#xff0c;请大家耐心看完&#xff01; 软件测试职业成长图 第一阶…

数字信号的产生与检测——DSP学习笔记六

本专栏的博客的图片大部分来源于老师的PPT&#xff0c;本博客只是博主对于上课内容的知识结构的分析和梳理。 几种数字信号的产生 正弦波信号 多项式逼近(除了泰勒展开&#xff0c;还有一种方法是切比雪夫逼近法&#xff0c;感兴趣可以自己去了解一下&#xff09; 查找表 核心思…

HDFS分布式文件存储系统

1-1 HDFS的存储机制 按块&#xff08;block&#xff09;存储 hdfs在对文件数据进行存储时&#xff0c;默认是按照128M(包含)大小进行文件数据拆分&#xff0c;将不同拆分的块数据存储在不同datanode服务器上 拆分后的块数据会被分别存储在不同的服务器上 副本机制 为了保证hdfs…

python环境安装jupyter

安装完毕之后下一步可以参考&#xff1a;配置jupyter的启动路径-CSDN博客 1 前提条件&#xff1a;python环境 系统&#xff1a;win10 python&#xff1a;本地已经有python&#xff0c;可以查看本地的python版本&#xff1a; C:\Users\PC>python --version Python 3.8.10 …

腾讯企点点击网址系统默认Google浏览器无法打开

最近更新了Chrome&#xff0c;企点里的信息无法自动完成链接跳转。 但是无法看卡在哪里。用了同事推荐的方法。把默认应用改成其他浏览器先测试。 其他浏览器没有问题&#xff0c;那就是Google浏览器有问题。尝试直接到软件目录双击打开。会弹出用户账户控制界面&#xff0c;询…

解决Blender导出FBX文件到Unity坐标轴错误的问题

发现Blender的模型导入到Unity里面有问题,简单研究了下发现是坐标系不同,Unity使用的是左手坐标系,Blender使用的是右手坐标系 。 下面直接将如何解决 首先忽略Blender的右手坐标系以及Z轴朝上的事&#xff0c;依照unity坐标系情况修改模型物体的旋转&#xff0c;以Blender猴…

Hystrix断路器

Hystrix断路器 概述分布式系统面临的问题什么是Hystrix 服务熔断什么是服务熔断添加方法 服务降级什么是服务降级实现方法 服务监控hystrixDashboard 概述 分布式系统面临的问题 复杂分布式体系结构中的应用程序有数十个依赖关系&#xff0c;每个依赖关系在某些时候不可避免地…

Python网络数据抓取(3):Beautiful Soup

Beautiful Soup 这个库通常被称为Beautiful Soup 4&#xff08;BS4&#xff09;。它主要用来从HTML或XML文件中抓取数据。此外&#xff0c;它也用于查询和修改HTML或XML文档中的数据。 现在&#xff0c;让我们来了解如何使用Beautiful Soup 4。我们将采用上一节中使用的HTML数据…

【优质书籍推荐】ChatGLM3大模型本地化部署、应用开发与微调

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

Latex入门教学——常用语句介绍

目录 一、导言 二、正文 三、图片 四、公式 五、表格 六、参考文献 LaTex模板下载 IEEE模板&#xff1a;IEEE Article Templates - IEEE Author Center Journals通用模板&#xff1a;Overleaf, Online LaTeX Editor其他方法&#xff1a;百度&#xff0c;CSDN等。 一、导…

华为校招机试 - 满二叉搜索树查找(20240424)

在线OJ测试 题目详情 - 满二叉搜索树查找 - HydroOJ 题目描述 给定 (2^n) - 1 个不同的整数(1 ≤ n ≤ 10,n 为整数),构建一棵平衡满二叉搜索树。 二叉搜索树定义如下: 节点的左子树只包含小于当前节点的数节点的右子树只包含大于当前节点的数所有左子树和右子树自身必…