题目描述
难度分:1800
输入n(1≤n≤1000),m(1≤m≤1000),k(1≤k≤n)和n×m个小写字母。
用这nm个字母,构造n个长为m的字符串,这n个字符串需要按照字典序从小到大排列。
最小化第k个字符串的字典序,然后输出这n个字符串。
输入样例1
3 2 2
abcdef
输出样例1
af
bc
ed
输入样例2
2 3 1
abcabc
输出样例2
aab
bcc
算法
贪心构造
思路不难,但是写起来还挺繁琐的。先遍历s,预处理出一个字母频数表cnt,然后依次考虑第1个位置到第m个位置。假设构造出来的n个字符串存储在矩阵matn×m中,分为以下两种情况:
- 考虑所有字符串的首字母,对于第k个字符串的首字母,检验它最小可以是哪个字母。非常容易检验,按照字典序遍历字母频数表cnt,看前几个字母的频数之和能够不小于k,第k个字符串的首字母就应该是满足Σcletter=acnt[letter]≥k的最小字母c,而前k−1个字符串的首字母按照字典序,用不大于c的字母填好即可。
- 考虑非首字母,假设当前考虑第j个字母(j>1),检查第j−1列是从哪一行l开始满足mat[l][j−1]=mat[k][j−1]的。此时说明矩阵mat的第j列从第l行到第k行都是字母mat[k][j−1],此时就要求第j列满足第l行到第k行的字母是单调不减的,又按照第一种情况的策略填字母(因为第j−1列已经满足mat[row][j−1]<c,row<l,所以第j列不用考虑第1到l−1行也能保证前面的字符串字典序更小)。看当前剩下的字母中,前几个字母的频数之和能够不小于k−l+1,第k个字符串的第j个字母就应该是满足Σcletter=acnt[letter]≥k−l+1的最小字母c,而第l行到第k−1行就按照字典序,用不大于c的字母填好即可。
遍历完m列之后,最小的第k个字符串就已经构造好了,其他位置的字符串长什么样已经不重要。此时矩阵mat中还有一些位置是没有填字母的,把剩余字母按照字典序排列好,然后按行充填矩阵中的所有空位即可。
复杂度分析
时间复杂度
这个算法本质上就是在充填大小为n×m的字符矩阵mat,只是对于第k行的关键列位置需要遍历字母a
~z
表来确定每列所要填的最小字母。因此,算法的时间复杂度为O(nm)。
空间复杂度
构造出来的字符矩阵mat消耗的空间为O(nm)。s串对应的每个字母的频数表cnt消耗的空间为O(Σ),其中Σ表示字符集大小,本题中为26。s串是输入,不算做额外空间,因此整个算法的额外空间复杂度应该为O(nm+Σ)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1001;
int n, m, k, cnt[30];
char s[N*N], mat[N][N];
int main() {
scanf("%d%d%d", &n, &m, &k);
scanf("%s", s + 1);
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= n*m; i++) {
cnt[s[i] - 'a']++;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
mat[i][j] = '.';
}
}
for(int j = 1; j <= m; j++) {
// 尝试让当前位置为字母c
if(j == 1) {
int sum = 0;
char kc;
for(char c = 'a'; c <= 'z'; c++) {
sum += cnt[c - 'a'];
if(sum >= k) {
kc = c;
break;
}
}
int i = 1;
for(char c = 'a'; c <= kc && i <= k; c++) {
while(cnt[c - 'a'] > 0 && i <= k) {
mat[i][j] = c;
cnt[c - 'a']--;
i++;
}
}
}else {
int l = 0;
char c = mat[k][j - 1];
for(int i = 1; i <= k; i++) {
if(mat[i][j - 1] == c) {
l = i;
break;
}
}
int sum = 0;
char kc;
for(char c = 'a'; c <= 'z'; c++) {
sum += cnt[c - 'a'];
if(sum >= k - l + 1) {
kc = c;
break;
}
}
int i = l;
for(char c = 'a'; c <= kc && i <= k; c++) {
while(cnt[c - 'a'] > 0 && i <= k) {
mat[i][j] = c;
cnt[c - 'a']--;
i++;
}
}
}
}
vector<char> remain;
for(int i = 0; i < 26; i++) {
while(cnt[i] > 0) {
remain.push_back('a' + i);
cnt[i]--;
}
}
int index = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(mat[i][j] == '.') {
mat[i][j] = remain[index++];
}
printf("%c", mat[i][j]);
}
puts("");
}
return 0;
}