題目描述
給定一些字符串(只包含小寫字母),要求將他們串起來構成一個字典序最小的字符串。
輸入格式
第一行T,表示有T組數據。
接下來T組數據
每組第一行一個正整數n,表示字符串個數。
接下來n行,每行一個字符串(長度不超過100)。
輸出格式
T行,每行一個字符串。
數據規模和約定
T<=7000,n<=100;
樣例輸入
1
3
a
b
c
樣例輸出
abc
思路
對於整個鏈接過程,我們需要先的到所有的可用字符串后在進行排序鏈接,所以簡單的排序
無法解決該問題。
首先思考字符串大小比較算法。如果第一位相同,則看第二位,以此類推如果其中一個達到
最后一位只需要對該字符串長度取余即可。於是便可得到比較字符串函數check();
接着思考如果只用兩個棧維護結果。那么就是每次來一個新的字符串x,那么我們會與棧頂
元素進行比較q.top()。如果x>q.top();那么直接壓入一個x即可,反之則將棧頂元素彈入
預備棧s中。知到找到比x大的字符串或者q中沒有字符串,將x壓入棧中,並將s中的元素依
次放回,那么便可使得整體字典序最小。(該方法可通過90%)
最后便是最終的優化:
首先上述方法,當數據較為極限時,每次需要將全部元素取出,壓入新來的元素,再將全部
元素放回,如此時間復雜度將會非常高。通過思考,我們發現,首字母小的一定在前面。
所以我們可以開26個棧,第0個棧存放開頭是a的所有元素,第1個棧存開頭是b的所有元素,
以此類推。從而我們每次插入時間只需找到首字母相同的棧進行排序即可。這樣插入操作的
時間復雜度便可得到有效的控制。便得到最終結果。(Accept!)
(單調棧) $O(n^2/2)$
時間復雜度分析
優化后如果全部元素首字母都相同,最壞情況下,第2次插入取出1個元素,第3次去2個以此類推
當n為100時,最壞情況下與優化前復雜度相同,為1+2+……+99=4950≈5000,最終復雜度為5000*T
=3.5e7。可以有效accept。
C++ 代碼
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;
const int N=110;
stack<string> q[27];
stack<string> s;
bool check(string x,string y)
{
int n=x.size(),m=y.size();
int k=max(n,m);
for(int i=0;i<k;i++)
{
if(x[i%n]<y[i%m]) return true;
if(x[i%n]>y[i%m]) return false;
}
return false;
}
void handel(stack<string> &q,stack<string> &s,string x)
{
if(!q.size())
{
q.push(x);
return;
}
string str=q.top();
while(!check(x,str) and q.size())
{
q.pop();
s.push(str);
if(q.size()) str=q.top();
else break;
}
q.push(x);
while(s.size())
{
string n=s.top();
s.pop();
q.push(n);
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
string str0;
for(int i=0;i<n;i++)
{
cin>>str0;
int t=str0[0]-'a';
handel(q[t],s,str0);
}
string res;
for(int i=0;i<26;i++)
{
while(q[i].size())
{
res=res+q[i].top();
q[i].pop();
}
}
cout<<res<<endl;
}
return 0;
}