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