1. 程式人生 > 其它 >Mac便捷螢幕截圖工具

Mac便捷螢幕截圖工具

Trie樹 上拓撲。

為什麼這道題目要建立 Trie樹 呢...因為對於兩個字串,比較他們的字典序大小無非就是比較他們第一個相當的字元是什麼,也就是說,如果這兩個字串在第 \(k\) 位開始不一樣,那麼他們也會在 \(k\) 對應的節點分別走向 \(k\) 不同的兒子。

暴力的思路無非就是暴力列舉、暴力判斷吧...列舉應該是不能優化了(?),而判斷可以藉助 Trie樹 來優化判斷呀。存在大小關係...是不是可以將大小關係變為一個有向圖呢?如果這個字元不可能是解的話,一定存在一組字元 \(u\)\(v\) 使得 \(u\) 既要優先於 \(v\)\(v\) 也要優先與 \(u\)...

對於一個字串,我們先假定這個字串是字典序最小的,那麼是不是就可以確定每個字元之間的大小關係呢...所以我們可以對於這個欽定的字串 \(s\) 的每一位對應的 Trie樹 上的節點 \(id\) ,遍歷 \(id\) 的每一個子結點(因為 Trie樹 上並沒有記錄每個點存在的子結點有幾個,所以只能 \(0\)\(25\) 來列舉),如果這個子結點有不同於 \(s\) 這一位的字元 \(j\) ,並且 \(id\) 存在 \(j\) 這個兒子,並且 \(tmp\) 暫時沒有與 \(j\) 確定大小關係,那麼很明顯 \(tmp\) 是要優先於 \(j\) 的 ,因此 \(tmp\)\(j\)

連上一條邊。

很顯然,如果存在一個可以的排列的話,這個有向圖一定是一個 DAG !每個字元都存在一個固定字元排在它的前一位...

於是通過這個迴圈,我們確定了優先順序...

ll id=0,len=x.length();
    memset(e,0,sizeof(e));
    memset(in,0,sizeof(in));
    for(register int i=0;i<len;++i){
        if(ed[id])  return 0;
        ll tmp=x[i]-'a';
        for(register int j=0;j<26;++j)
            if(tmp!=j&&trie[id].ch[j]&&!e[tmp][j]){
                e[tmp][j]=1;
                ++in[j];
            }
        id=trie[id].ch[tmp];
    }

如何判斷是不是 DAG 呢...拓撲排序啊。

如果一個點始終不能將入度變為 \(0\) ,說明這個有向圖是存在環的!這樣就很好判斷了呀~如果存在環說明一定存在一個 \(u\) 和一個 \(v\) 使得 \(u\) 要優先於 \(v\)\(v\) 也要優先於 \(u\)!

至此,這道題目就做完了呀...

拓撲排序:

queue<ll>q;
    while(!q.empty())    q.pop();
    for(register int i=0;i<26;++i)  
        if(!in[i])  
            q.push(i);
    while(!q.empty()){
        ll u=q.front();q.pop();
        for(register int i=0;i<26;++i)
            if(e[u][i]){
                --in[i];
                if(!in[i])
                    q.push(i);
            }
    }

判斷是否為 DAG:

for(register int i=0;i<26;++i)
        if(in[i])
            return 0;
    return 1;

完整程式:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,cnt,node,ed[400001],in[30],ans,e[30][30],ok[30001];
string s[30001],c;
struct Node{
    ll fail=0,num=0,ch[25];
}trie[400001];

inline ll read(){
    ll x=0,f=0;char c=getchar();
    while(!isdigit(c))  f|=c=='-',c=getchar();
    while(isdigit(c))   x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return f?-x:x;
}

inline void build(ll x){
    ll id=0,len=s[x].length();
    for(register int i=0;i<len;++i){
        ll tmp=s[x][i]-'a';
        if(!trie[id].ch[tmp])
            trie[id].ch[tmp]=++cnt;
        id=trie[id].ch[tmp];
    }
    ed[id]=1;
}

inline int find(string x){
    ll id=0,len=x.length();
    memset(e,0,sizeof(e));
    memset(in,0,sizeof(in));
    for(register int i=0;i<len;++i){
        if(ed[id])  return 0;
        ll tmp=x[i]-'a';
        for(register int j=0;j<26;++j)
            if(tmp!=j&&trie[id].ch[j]&&!e[tmp][j]){
                e[tmp][j]=1;
                ++in[j];
            }
        id=trie[id].ch[tmp];
    }
    queue<ll>q;
    while(!q.empty())    q.pop();
    for(register int i=0;i<26;++i)  
        if(!in[i])  
            q.push(i);
    while(!q.empty()){
        ll u=q.front();q.pop();
        for(register int i=0;i<26;++i)
            if(e[u][i]){
                --in[i];
                if(!in[i])
                    q.push(i);
            }
    }
    for(register int i=0;i<26;++i)
        if(in[i])
            return 0;
    return 1;
}

int main(){
    n=read();
    for(register int i=1;i<=n;++i){
        cin>>s[i];
        build(i);
    }
    for(register int i=1;i<=n;++i)
        if(find(s[i])){
            ++ans;
            ok[i]=1;
        }
    printf("%lld\n",ans);
    for(register int i=1;i<=n;++i)
        if(ok[i])
            cout<<s[i]<<endl;
    return 0;
}