그냥 쭉 쓰다보니 코드가 너무 길어졌다.
새로 정의한 사전순 문자열로부터 문자의 순서를 재정의한다.
예제 같은 경우는 l->u, l->k, u->k, k->a 이렇게 4개
1. 올바른 순서가 없는 경우
: 모순이 존재한다는 것. 싸이클이 있다는 것이다.
: 플로이드 돌린 후에 다시 자신으로 돌아오는 경로가 있으면 싸이클임.
2. 가능한 순서가 여러개인 경우
: 사전의 첫 글자가 여러개이거나, (첫 글자로부터) k번째 숫자에 올 수 있는 수가 여러개인 경우.
위에 적은 것만 구현했는데 코드가 1.6KB나 된다. 소스 정말 더럽다.
#include <bits/stdc++.h>
using namespace std;
#define INF 987654321
int main(){
int n;
scanf("%d ", &n);
bool used[26]={};
bool v[26][26]={};
char s[100][11];
for(int i=0; i<n; ++i){
scanf("%s ", s[i]);
int clen = strlen(s[i]);
for(int j=0; j<clen; ++j) used[s[i][j]-'a'] = true;
if(i == 0) continue;
int len = min(clen, (int)strlen(s[i-1]));
for(int j=0; j<len; ++j){
if(s[i-1][j] != s[i][j]){
v[s[i-1][j]-'a'][s[i][j]-'a'] = true;
break;
}
}
}
for(int k=0; k<26; ++k){
for(int i=0; i<26; ++i){
for(int j=0; j<26; ++j){
v[i][j] |= v[i][k] & v[k][j];
}
}
}
// contradiction
for(int i=0; i<26; ++i)
if(v[i][i]) return puts("!"), 0;
vector<int> start;
for(int i=0; i<26; ++i){
if(!used[i]) continue;
bool hasIndegree = false;
for(int j=0; j<26; ++j) hasIndegree |= v[j][i];
if(!hasIndegree) start.push_back(i);
}
if(start.size() > 1) return puts("?"), 0;
int dist = 1;
vector<pair<int, char>> res(26, {0, 0});
for(int i=0; i<26; ++i) res[i].second = i+'a';
queue<int> q;
q.push(start[0]);
res[start[0]].first = dist++;
while(!q.empty()){
int qs = q.size();
while(qs--){
int cur = q.front();
q.pop();
for(int i=0; i<26; ++i){
if(v[cur][i] && dist > res[i].first){
q.push(i);
res[i].first = dist;
}
}
}
dist++;
}
sort(res.begin(), res.end());
for(int i=1; i<res.size(); ++i){
if(res[i].first > 0 && res[i-1].first == res[i].first)
return puts("?"), 0;
}
for(int i=0; i<res.size(); ++i){
if(res[i].first > 0) putchar(res[i].second);
}
return 0;
}
댓글 없음:
댓글 쓰기