Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Think of each string as a whole block. You never split an , so the only question is whether the next block goes before or after the current built string.
When processing , the only two immediate candidates are and . Since both have the same length, lexicographic comparison directly tells which current string is better.
Future strings do not mess up this comparison. Any later prepended strings will appear before both candidates, and any later appended strings will appear after both candidates. So the current choice is still preserved inside the same surrounding context.
Therefore, you can greedily keep the smallest possible current string after every step: if , prepend; otherwise append.
The constraints are tiny in total length: only characters overall. Actually building and comparing the two candidate strings each step is totally fine. No cursed suffix-array wizardry needed.
We process the strings in order. At step , suppose we have already built some string . The rules give exactly two choices:
So the greedy move is very tempting: keep the smaller of these two strings.
The main thing to prove is that this local choice does not backfire later. And yeah, for once the obvious greedy is actually right. Codeforces 800 moment, but in a nice way.
Key Observation
After some step, future strings can only be added to the beginning or the end of the entire current string.
So imagine two possible current strings and of the same length, where . Later operations may add some big prefix in front and some suffix at the end. The final comparison becomes:
The prefix is identical, so it cannot affect which one wins. Once comparison reaches the middle part, it compares and , and since , the first final string is still smaller.
The suffix also does not matter, because the strings and have equal length. If they differ somewhere, the decision happens there. If they are equal, then either choice is equivalent anyway.
That means: if one current string is lexicographically smaller than another after step , it will never become worse because of later operations.
Greedy Algorithm
Start with .
For each string :
front = a_i + s.back = s + a_i.That is it. No hidden DP goblin in the walls. The whole problem is realizing that later additions wrap both candidates in the same way, so the lexicographic order survives.
Why This Is Correct
We prove by induction that after processing the first strings, our greedy string is the lexicographically smallest string obtainable from those strings.
Base case: before processing anything, is empty, so it is trivially optimal.
Inductive step: assume after steps, greedy has the minimum possible string . At step , any valid construction must place either before or after some string obtainable from the first strings.
Because is already the smallest possible previous string, replacing any larger previous string with cannot make the result worse. Then the only remaining choice is between:
and n
Greedy picks the smaller one, so it gets the best possible string after step .
By induction, after all steps, greedy outputs the lexicographically smallest reachable string.
Complexity
The total length of all input strings is at most . Each step may build and compare strings of length up to the current total length, so the solution easily fits.
Time complexity: in the simplest implementation, where is the total input length of a test case.
Memory complexity: .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s;
for (int i = 0; i < n; i++) {
string a;
cin >> a;
string front = a + s;
string back = s + a;
s = min(front, back);
}
cout << s << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s;
for (int i = 0; i < n; i++) {
string a;
cin >> a;
string front = a + s;
string back = s + a;
s = min(front, back);
}
cout << s << '\n';
}
return 0;
}