1. 程式人生 > 實用技巧 >Codeforces 1424M - Ancient Language (拓撲排序)

Codeforces 1424M - Ancient Language (拓撲排序)

Bubble Cup 13 - Finals [Online Mirror, unrated, Div. 2] M. Ancient Language


題意

給定一個共有\(n\)頁的字典,每一頁有\(k\)個單詞

已知這本字典使用的古老語言的字母是英文字母的子集不一定\(26\)個英文字母都被使用到

問你是否存在一種字典序滿足這本字典內單詞原來的排列順序

存在的話只需要輸出任意一種即可,不存在輸出IMPOSSIBLE


限制

\(1\le n,k\le 1000\)

\(0\le p_i\lt 1000\)




思路

將給定的所有單詞按照題意進行排序,頁碼小的放前,頁碼相同的按照輸入順序排序

然後考慮所有的相鄰單詞,這裡以\(i,j\)

來描述(\(j=i+1\)

如果\(i,j\)兩個單詞相同,不考慮

如果\(i,j\)兩個單詞不同,且\(i\)\(j\)的字首,不考慮

如果\(i,j\)兩個單詞不同,且\(j\)\(i\)的字首,明顯這樣不可能有滿足的字典序,輸出IMPOSSIBLE

如果\(i,j\)兩個單詞不同,且不滿足上述兩種條件,則只看出現不同字母時下標最小的那個位置\(k\)

\(a\)表示第\(i\)個單詞下標為\(k\)的字母,令\(b\)表示第\(j\)個單詞下標為\(k\)的字母

可以發現,\(a\)的字典序應該是小於\(b\)的,所以將\(26\)個英文字母看作點,將\(a\rightarrow b\)

連邊,同時標記\(b\)的入度\(+1\)

明顯的,只有當最後產生的圖是一個DAG時才存在一個解,所以我們每次找入度為\(0\)的點加入答案中即可,直接拓撲排序搜尋即可




程式

(826ms/2000ms)

// StelaYuri
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define pb push_back
#define eb emplace_back
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const double angcst=PI/180.0;
const ll mod=998244353;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}

struct node
{
    string s;
    int i,j; //頁碼、位置
    bool operator < (const node& a) const
    {
        if(i^a.i)
            return i<a.i;
        return j<a.j;
    }
}str[1000050];

int ind[30]; //入度
bool to[30][30]; //連邊
bool vis[30]; //是否出現過該字母

void solve()
{
    int n,k;
    cin>>n>>k;
    
    int tot=0;
    rep(i,1,n)
    {
        int id;
        cin>>id;
        rep(j,1,k)
        {
            ++tot;
            cin>>str[tot].s;
            for(char c:str[tot].s)
                vis[c-'a']=true;
            str[tot].i=id;
            str[tot].j=j;
        }
    }
    
    sort(str+1,str+1+tot);
    
    repp(i,1,tot)
    {
        int len=min(str[i].s.size(),str[i+1].s.size());
        bool flag=false;
        repp(j,0,len)
        {
            if(str[i].s[j]!=str[i+1].s[j])
            {
                int a=str[i].s[j]-'a';
                int b=str[i+1].s[j]-'a';
                if(!to[a][b])
                    ind[b]++;
                to[a][b]=true; //a->b連邊
                flag=true;
                break;
            }
        }
        if(!flag)
        {
            if(str[i].s.size()>str[i+1].s.size()) //如果i+1是i的字首,顯然不存在解
            {
                cout<<"IMPOSSIBLE\n";
                return;
            }
        }
    }
    
    int siz=0;
    repp(i,0,26)
        siz+=(vis[i]?1:0);
    
    queue<int> q;
    vector<int> ans;
    repp(i,0,26)
        if(vis[i]&&ind[i]==0) //挑選入度為0的點
            q.push(i);
    
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        ans.pb(cur);
        repp(it,0,26)
        {
            if(to[cur][it])
            {
                ind[it]--;
                if(ind[it]==0)
                    q.push(it);
            }
        }
    }
    
    if(ans.size()!=siz)
    {
        cout<<"IMPOSSIBLE\n";
        return;
    }
    
    for(int it:ans)
        cout<<char(it+'a');
}
int main()
{
    closeSync;
    //multiCase
    {
        solve();
    }
    return 0;
}