题意很简单 我们可以随意防止字符串 按照从上到下 如果最后一层某个位置没有字符串 那么上面的字符串就会掉下来到最后一层 求字典序最小的最下层的字符串
首先 最朴素的思想 我们会找出当前最小长度的字符串 长度k 然后截取所有字符串的前k个前缀元素 然后排序 找到最小的一个 将整个字符串填进去 然后从k到下一个长度如此反复 但是排序和遍历过多 时间复杂度过大 这是从从左到右遍历比较后缀数字的大小
这个时候 我们可以用类似于后缀数组的倍增思想进行排序
我们从后往前遍历 用数组res 存储所有长度大于当前长度len的数组的索引
然后维护一个rk数组 表示数组的排名(按照字典序)
我们从后往前遍历 对于最后一位 直接按照最后一位的所有数组当前位置的字符进行排序
往前遍历的时候 我们可以维护一个vector<arrey<int,3>> 分别存储 {当前位置的字符 当前位置的后缀 在上一次遍历的排名 以及数组索引 }
排序完成后 第一名的就是当前位置的最优索引 然后更新一下排名 便于往前遍历的时候复用
然后进行排序 这样可以复用后缀大小的关系 直接解决包含当前位的后缀的大小的问题
最后生成答案的时候 按照每个分界位置的最优选择进行生成即可
代码如下:
cpp
#include <bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin>>n;
vector<vector<int>>res;
vector<vector<int>>a(n+1);
int maxlen=0;
for(int i=1;i<=n;i++){
int k;
cin>>k;
maxlen=max(maxlen,k);
a[i].assign(k+1,0);
for(int j=1;j<=k;j++){
cin>>a[i][j];
while(res.size()<=j)res.push_back({});
res[j].push_back(i);
}
}
vector<int>rk(n+1,-1);
vector<int>minidx(maxlen+1,0);
for(int i=maxlen;i>=1;i--){
vector<array<int,3>>cur;
for(auto j:res[i]){
cur.push_back({a[j][i],rk[j],j});
}
sort(cur.begin(),cur.end());
minidx[i]=cur[0][2];
int rkk=0;
for(auto j:cur){
rk[j[2]]=++rkk;
}
}
vector<int>ans;
while(ans.size()<maxlen){
int tmp=ans.size();
auto &v =a[minidx[tmp+1]];
for(int i=tmp+1;i<v.size();i++){
ans.push_back(v[i]);
}
}
for(auto x:ans)cout<<x<<' ';
cout<<'\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)solve();
return 0;
}