給你一個字符串,要求你設計一種鍵盤,這個鍵盤用一行表示,其中在題目字符串中相鄰的字符,在你的鍵盤上也要相鄰。
我們用三個數組模擬一個雙向鏈表,pre[i]表示i字符前一個字符,nxt[i]表示i字符後一個字符,vis[i]標記i字符是否在鏈表中出現。
輸出的時候我們先把鏈表輸出,再把剩餘的隨便輸出就i行。
一個字符在鏈表出現過後,它的位置就是固定了的,如果前後爲空可以添加,但不能使它調到別的地方。不能出現循環鏈表。
因爲輸出的一行,某兩個相鄰的一定會被拆到鏈表兩頭,就不相鄰了。
具體細節在代碼註釋裏。
注意特判輸入只有一個字符的情況。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=205;
char s[N];
int pre[31],nxt[31],vis[31];
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>s;
for(int i=0;i<26;i++) pre[i]=nxt[i]=-1,vis[i]=0;//初始化,-1代表前後沒有字符,0代表沒有出現過
int l=strlen(s),flag=1;
if(l==1)
{
printf("YES\n");
for(int i=0;i<26;i++) printf("%c",i+'a');
printf("\n");
continue;
}
vis[s[0]-'a']=1;//先把第一個字符標記上
for(int i=1;i<l&&flag;i++)//從第二個字符開始,防止越界
{
int pree=s[i-1]-'a',tmp=s[i]-'a';//tmp表示當前字符,pree表示當前字符在輸入中的前一個字符
if(pre[pree]==tmp||nxt[pree]==tmp) continue;//滿足題目要求,不需要再進行了
flag=0;//標記字符是否成功放進鏈表
if(vis[tmp]) break;//pree一定已經在鏈表中了,此時tmp也在鏈表中
//一種情況tmp兩端都有元素,上面已經判斷過,兩端是pree的已經continue了,到這裏說明不是
//一種情況tmp一端有元素,另一端爲空,如果讓空的這端與pree相連,就成循環了
//鏈表中不可能兩端都沒有元素
if(nxt[pree]==-1)//tmp加在pree後面
{
nxt[pree]=tmp;
pre[tmp]=pree;
flag=1;
}
if(flag==0&&pre[pree]==-1)//tmp加在pre前面
{
pre[pree]=tmp;
nxt[tmp]=pree;
flag=1;
}
if(flag==1) vis[tmp]=1;//添加成功,標記在鏈表中出現了
}
if(flag==0) cout<<"NO"<<endl;//有節點添加不進去
else
{
cout<<"YES"<<endl;
for(int i=0;i<26;i++)
{
if(pre[i]==-1&&nxt[i]!=-1)//找到鏈表頭
{
int x=i;
while(x!=-1) //輸出鏈表
{
printf("%c",x+'a');
x=nxt[x];
}
break;
}
}
for(int i=0;i<26;i++) if(!vis[i]) printf("%c",i+'a');
cout<<endl;
}
}
//system("pause");
}