凱撒Cezar
題意
這題首先需要讀懂題:給出的序列 \(A_i\) 代表第 \(i\) 個字串排名為 \(A_i\)
拿樣例1來說,就是:第二個字串 "\(bc\)" 排名為1,第一個字串 "\(ab\)" 排名為2(說明 "\(bc\)" 這個字串的字典序比 "\(ab\)" 小)
(好像很多人都理解錯了)
思路
這題,一眼拓撲(或者變種 \(Floyd\))
- 轉換
首先,根據序列 \(A_i\) 我們可以進行字元之間的連邊——確定字典序大小關係(排在前面的連向排在後面的)
然後我們就將字串間的關係轉化為有向圖,那麼接下來就是赤裸裸的拓撲了啊!
- 拓撲
這裡會遇到第一個無解(即 "\(NE\)")的情況:跑拓撲的時候,一開始佇列就為空(說明不存在入度為0的點,則形成了環)
判了上面這種情況,接下來就是拓撲板子。用 \(ans\) 和 \(ans2\) 記錄拓撲序,用 \(viss\) 標記這個點被找到過
接下來,我們將 \(ans\) 從小到大排序,再將 \(ans\) 和 \(ans2\) 一一對應存入 \(anss\) 中(最終輸出的陣列)。我們根據下面的圖來理解為什麼排序後直接對應即可:
應該可以理解吧?如果不是很清楚的話,可以把樣例1、3都畫一下
對應的程式碼段就是:
sort(ans+1,ans+1+sum); for(int i=1;i<=sum;i++) { anss[ans2[i]]=ans[i]; } for(int i=0;i<=25;i++) { if(vis[i]&&!viss[i]) { puts("NE"); return 0; } if(!viss[i]) anss[i]=i; }
- 無解
因為這題是SPJ,所以當跑拓撲序的時候可以一次多點入隊
然後就是比較極端的兩組資料(還是比較考細節吧):
5
c
cc
ccc
cccc
ccccc
1 2 3 4 5
out:
DA
abcdefgihjklmnopqrstuvwxyz
5
d
dd
ddd
dddd
ddddd
1 2 3 5 4
out:
NE
發現區別了嗎?
第一組資料是因為所有字串都是相同的一個字元,且排名在前的字串的長度永遠不大於排名在後的字串
而第二組資料就是這裡出鍋,所以是 "\(NE\)" (排名第四的字串長度大於排名第五的字串)
那麼我們就可以在跑拓撲之前特判一下這兩種情況:
設 \(cnt\) 記錄有多少個不同的字元
-
若 \(cnt==1\),且 \(len[i]>len[j](i<j)\),則是上面第二組資料的情況,輸出無解
-
連完邊後,再判斷若 \(cnt==1\) ,則是上面第一組資料的情況,直接輸出有解然後結束程式即可
還有一種無解的情況就是:若跑完拓撲序後發現,原本是有大小關係的字元而沒有在拓撲序中出現,那麼也是無解,即:
for(int i=0;i<=25;i++) {
if(vis[i]&&!viss[i]) { //vis標記在大小關係比較中出現過的點
puts("NE");
return 0;
}
if(!viss[i]) anss[i]=i;
}
程式碼
嗯,思路和需要注意的細節就是上面講的這麼多了,接下來直接看程式碼吧:
#include <bits/stdc++.h>
using namespace std;
string s[1001];
int sum,vis[1001],viss[1001],ans[20010],ans2[20010];
int n,tot,cnt,a[1001],in[1001],head[200010],anss[20010];
struct node {
int to,net;
} e[200010];
void add(int u,int v) {
e[++tot].to=v;
e[tot].net=head[u];
head[u]=tot;
}
int topo() {
queue<int> q;
for(int i=0;i<=25;i++) {
if(!in[i]&&vis[i]) q.push(i);
}
if(!q.size()) return -1;
while(!q.empty()) {
bool flag=false;
int x=q.front();
q.pop();
if(viss[x]) return -1;
viss[x]=1;
ans[++sum]=x;
ans2[sum]=x;
for(int i=head[x];i;i=e[i].net) {
int v=e[i].to;
if(--in[v]==0) {
flag=true;
q.push(v);
}
}
}
return sum;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
cin>>s[i];
}
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++) {
int k=0,kk=0;
while(k<s[a[i]].size()&&kk<s[a[i+1]].size()) {
if(s[a[i]][k]!=s[a[i+1]][kk]) {
add(s[a[i]][k]-'a',s[a[i+1]][kk]-'a');
if(!vis[s[a[i]][k]-'a']) cnt++,vis[s[a[i]][k]-'a']=1;
if(!vis[s[a[i+1]][kk]-'a']) cnt++,vis[s[a[i+1]][kk]-'a']=1;
in[s[a[i+1]][kk]-'a']++;
break;
}
k++;kk++;
}
if(cnt==0&&s[a[i]].size()>s[a[i+1]].size()) {
puts("NE");
return 0;
}
}
if(cnt==0) {
puts("DA");
for(int i=0;i<=25;i++) cout<<char(i+'a');
return 0;
}
int t=topo();
if(t==-1) {
puts("NE");
return 0;
}
sort(ans+1,ans+1+sum);
for(int i=1;i<=sum;i++) {
anss[ans2[i]]=ans[i];
}
for(int i=0;i<=25;i++) {
if(vis[i]&&!viss[i]) {
puts("NE");
return 0;
}
if(!viss[i]) anss[i]=i;
}
puts("DA");
for(int i=0;i<=25;i++) {
cout<<char(anss[i]+'a');
}
return 0;
}