环状串的字典序

news/2024/9/23 8:35:40/

【题目描述】

长度为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;}}}//如果找到多个最小字母,依次判断其后面的最小字母的数量,直到其数量为1if(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;
}


http://www.ppmy.cn/news/1444868.html

相关文章

Github Action Bot 开发教程

Github Action Bot 开发教程 在使用 Github 时&#xff0c;你可能在一些著名的开源项目&#xff0c;例如 Kubernetes&#xff0c;Istio 中看到如下的一些评论&#xff1a; /lgtm /retest /area bug /assign xxxx ...等等&#xff0c;诸如此类的一些功能性评论。在这些评论出现…

python学习笔记----函数(五)

一、函数介绍 在 Python 中&#xff0c;函数是一个组织好的、可重用的代码块&#xff0c;用来执行一个单一的、相关的动作。函数提供了代码的模块化和代码复用的能力。它可以接受输入参数&#xff0c;并可以返回一个结果。函数在 Python 编程中是基本的构建块之一。 二、函数…

云计算中的网络服务

网络服务是云计算平台不可或缺的一部分&#xff0c;为用户提供构建、管理、保护云环境中网络资源的能力。以下是对列举的七种网络服务——虚拟私有云&#xff08;VPC&#xff09;、负载均衡、内容分发网络&#xff08;CDN&#xff09;、云防火墙、专用网络连接&#xff08;专线…

图像处理软件Photoshop 2024下载及安装步骤

简介 Adobe Photoshop&#xff0c;简称Ps&#xff0c;是由Adobe公司开发和发行的图像处理软件。 Photoshop主要处理以像素所构成的数字图像&#xff0c;使用其众多的编修与绘图工具&#xff0c;可以有效地进行图片编辑和创造工作。PS 有很多功能&#xff0c;在图像、图形、文字…

【PostgreSQL】pg触发器介绍

注: 本文为云贝教育 刘峰 原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。 触发器是在对指定表执行指定更改操作&#xff08;SQL INSERT、UPDATE、DELETE 或 TRUNCATE 语句&#xff09;时自动运行的一组操作…

Node.js 的 fs 模块分析及其应用

fs 模块&#xff0c;作为 Node.js 平台中的一个核心组件&#xff0c;主要负责处理文件系统相关的操作。该模块提供了一系列用于文件管理的功能&#xff0c;例如文件的读取、写入、更新以及删除等。 应用场景分析 fs 模块的应用范围广泛&#xff0c;下面是一些典型的使用实例&…

【百度Apollo】探索自动驾驶:Apollo 新版本 Beta 全新的Dreamview+,便捷灵活更丰富

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引入一、Dreamview介绍二、Dreamview 新特性2.1、基于模式的多场景——流程更简洁地图视角调节&#xff1a;调试流…

【Leetcode每日一题】 综合练习 - 找出所有子集的异或总和再求和(难度⭐)(68)

1. 题目解析 题目链接&#xff1a;1863. 找出所有子集的异或总和再求和 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路与实现 为了求解给定整数数组的所有子集并将其异或和相加&#xff0c;我们可以采用递…