1. 程式人生 > >BZOJ3012 First!題解(Trie樹+拓撲排序)

BZOJ3012 First!題解(Trie樹+拓撲排序)

題目:BZOJ3012.
題目大意:給定一些字串,求在每一個串是否能在一個字典順序下字典序最小.

這道題一看到要拓撲排序就懵了,想了一下如何建圖也沒有想出來,這種建圖的套路題做的少啊…

首先我們先確定一個性質,就是當一個串有一個字首串也在給定串中時,這個串肯定不可能字典序最小了.

於是我們就發現這道題要查一個串是否有字首也在給定串中,就可以很自然的想到Trie樹.

想到Trie樹後,我們考慮一個串字典序最小時,優先讓前面的最小,也就是說我們可以從Trie的根開始往下,與當前節點相連的每一層邊中,字典序最小的串所對應的邊必須是最小的.

這時我們就想到,可以建一張圖以每個字元作為一個節點,當一個字元x必須大於另一個字元y時就連一條從x到y的有向邊,那是否矛盾的問題就變成了判環問題,於是我們就可以愉快的寫拓撲排序判環了.時間複雜度  

O ( 26 m + 2 6 2 n )
\ O(26m+26^2n) .

所以程式碼如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;
#define m(a) memset(a,0,sizeof(a))

const int N=30000,M=300000,C=26;

struct Trie{
  int s[C],cnt;
  Trie(){m(s);cnt=
0;} }tr[M+9]; int cn; void Build(){tr[cn=1]=Trie();} void Insert(char *c,int len){ int x=1; for (int i=1;i<=len;++i) if (tr[x].s[c[i]-'a']) x=tr[x].s[c[i]-'a']; else { tr[x].s[c[i]-'a']=++cn; tr[cn]=Trie(); x=cn; } ++tr[x].cnt; } int e[C+9][C+9],deg[C+9]; queue<int>q; bool topsort(){ for (int i=0;i<C;++i) if (!deg[i]) q.push(i); int t; while (!q.empty()){ t=q.front();q.pop(); for (int i=0;i<C;++i) if (e[t][i]){ deg[i]-=e[t][i]; if (!deg[i]) q.push(i); } } for (int i=0;i<C;++i) if (deg[i]) return false; return true; } bool Check(char *c,int len){ int x=1; for (int i=0;i<C;++i){ for (int j=0;j<C;++j) e[i][j]=0; deg[i]=0; } for (int i=1;i<=len;++i) if (tr[x].s[c[i]-'a']){ if (tr[x].cnt) return false; for (int j=0;j<C;++j) if (c[i]-'a'^j&&tr[x].s[j]) ++e[c[i]-'a'][j],++deg[j]; x=tr[x].s[c[i]-'a']; }else break; return topsort(); } vector<char> c[N+9],ans[N+9]; char tmp[M+9]; int len[N+9],n,m; int la[N+9],k; Abigail into(){ scanf("%d",&n); Build(); for (int i=1;i<=n;++i){ scanf("%s",tmp+1); len[i]=strlen(tmp+1); Insert(tmp,len[i]); c[i].push_back(0); for (int j=1;j<=len[i];++j) c[i].push_back(tmp[j]); } } Abigail work(){ for (int i=1;i<=n;++i){ for (int j=1;j<=len[i];++j) tmp[j]=c[i][j]; if (!Check(tmp,len[i])) continue; la[++k]=len[i]; ans[k].push_back(0); for (int j=1;j<=len[i];++j) ans[k].push_back(tmp[j]); } } Abigail outo(){ printf("%d\n",k); for (int i=1;i<=k;++i){ for (int j=1;j<=la[i];++j) putchar(ans[i][j]); puts(""); } } int main(){ into(); work(); outo(); return 0; }