原题
《二进制王国》
问题描述
二进制王国是一个非常特殊的国家,因为该国家的居民仅由0和1组成。
在这个国家中,每个家庭都可以用一个由0和1组成的字符串S来表示,例如101、000、111等。
现在,国王选了出 N户家庭参加邻国的庆典。为了符合王国的审美标准,我们需要选择一种排队顺序,使得最终形成的队伍在字典序上是最小的。
国王将这个任务交给了你,请你解决这个问题。
输入格式
第一行包含一个整数N(1≤N≤2×10^5),代表二进制家庭数量。
接下来输入N行,第i行输入一个二进制字符串Si表示第i户家庭。
数据范围保证:∑|Si|≤2×10^5,其中|Si|表示第i个字符串的长度。(即总数据长度小于2e5)
输出格式
输出一行一个字符串,表示字典序最小的排队情况。
输入样例
3
111
000
101
输出样例
000101111
网址:https://www.lanqiao.cn/problems/17035/learning/
思路简述
1、存储
由题,字符串总长度是2e5。若假设每个字符串的长度不超过2500,最多一共1000个字符字符串,则需要分配2.5e6字节的内存,大约是2.5MB。
而一次性性分配1e6字节的数据是可以接受的,大约是1MB,即char data[(int)1e6] 或 int data[int(1e5)]。所以char data[1000][2500]会报错(图一)。
故我们使用一个长度为2 * 2e5字节的字符串灵活储存所有的字符串数据,和一个长度为2e5的字符串指针数组来储存每个字符串数据的首地址。2 * 2e5是为了存储2e5个单字字符串和它们的结束符'\0'。
小结1:
最大分配内存:
int data[(int)1e5]; char data[(int)1e6];
2、最小字典序
首先什么是字典序?简单的说:"1", "10", "11"。第一位都是'1',往后比。在前段字符相同的情况下,长度短的在前,故"1"在最前面。接着往后比,据ASCII,'0' = 48,'1' = 49,48 < 49,所以"10"在"11"前。
所以我们只用将各个字符串数据以字典序排列就行了?
NO!
题目要求我们使得拼接后的字典序最小。
即:下列测试样例输出的结果应为10010,而非10100,即使"10"的字典序在"100"之前。
测试样例
2
10 100
测试结果
10010
因为"10010"的字典序在"10100"之前。
如何实现呢?
我们只需保证任意两个字符串数据拼接后的字典序最小即可。
即:比较str1+str2和str2+str1,而非str1和str2。
小结2:
最小字典序 ASCII '1', '10', '11' 拼接最小字典序 "10", "100", "10010" 比较str1+str2和str2+str1
源码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const int MaxLen = 2E5;
const int MaxSing = 2500; // I guess
int cmp();
int main() {
int N; // N strings
char str[2 * MaxLen]; // store all datas
char * pos[MaxLen]; // the position of strs
int p;
// ReadStr
scanf("%d", &N);
p = 0;
for (int i = 0; i < N; ++i) {
scanf("%s", &str[p]);
pos[i] = &str[p];
do { ++p; } while (str[p - 1] != '\0');
}
// qsort
qsort(pos, N, sizeof(pos[0]), cmp);
// output;
for (int i = 0; i < N; ++i) {
printf("%s", pos[i]);
}
return 0;
}
int cmp(const void * pos1,const void * pos2) {
const char * s1 = *((const char **) pos1);
const char * s2 = *((const char **) pos2);
char _s1[MaxSing * 2], _s2[MaxSing * 2];
strcpy(_s1, s1);
strcat(_s1, s2);
strcpy(_s2, s2);
strcat(_s2, s1);
return strcmp(_s1, _s2);
}