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; }