【[USACO12DEC]第一!First!】
一个串不能成为第一的情况有两种
-
另外一个单词是它的前缀
-
在分配字母表大小关系的时候出现了矛盾的情况
第一种很好判断,一旦我们在一个单词没有匹配完之前遇到一个结束标志,那么就说明另外一个单词是它的前缀
至于第二种,看到大小关系和是否矛盾我们很容易就联想到了拓扑排序
于是我们匹配的时候,发现某一层除了当前正在匹配的串以外还有其他字母,那么这些字母在我们构建的字母表中必须大于当前的这个字母,于是我们连一条有向边表示一下大小关系就好了
最后跑一遍拓扑判断一下是否有环就好了
#include<cstring>
#include<cstdio>
#include<iostream>
#include<string>
#define re register
#define maxn 300005
using namespace std;
struct node
{
int v,nxt;
}e[1005];
int son[maxn][26],flag[maxn];
std::string S[30005];
int len[30005];
int n,num,cnt,tot;
int head[26];
int ans[maxn];
inline void add_edge(int x,int y)
{
e[++num].v=y;
e[num].nxt=head[x];
head[x]=num;
}
inline void ins(int x)
{
int now=0;
for(re int i=0;i<len[x];i++)
{
if(!son[now][S[x][i]-'a']) son[now][S[x][i]-'a']=++cnt;
now=son[now][S[x][i]-'a'];
}
flag[now]=1;
}
inline int check(int x)
{
int now=0;
memset(e,0,sizeof(e));
int c[26];
memset(c,0,sizeof(c));
memset(head,0,sizeof(head));
num=0;
for(re int i=0;i<len[x];i++)
{
if(flag[now]) return 0;
for(re int j=0;j<26;j++)
if(son[now][j]&&S[x][i]-'a'!=j)
add_edge(S[x][i]-'a',j),c[j]++;
now=son[now][S[x][i]-'a'];
}
int cur=0;
int q[27];
memset(q,0,sizeof(q));
for(re int i=0;i<26;i++)
if(!c[i]) q[++cur]=i;
for(re int i=1;i<=cur;i++)
{
for(re int j=head[q[i]];j;j=e[j].nxt)
{
c[e[j].v]--;
if(!c[e[j].v]) q[++cur]=e[j].v;
}
}
return cur==26;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(re int i=1;i<=n;i++)
{
cin>>S[i];
len[i]=S[i].size();
ins(i);
}
tot=0;
for(re int i=1;i<=n;i++)
if(check(i)) ans[++tot]=i;
cout<<tot<<endl;
for(re int i=1;i<=tot;i++)
cout<<S[ans[i]]<<endl;
return 0;
}