#include<stdio.h>
char s[30057][357];
int next[300057][29];
int cnt[300057];
int que[300057];
int sz;
int head[29];int adj[300057];int v[300057];int ec;
int indegree[29];
void tinsert(char *s)
{
int i,j,k;
j=0;
for(i=0;s[i];++i)
{
k=s[i]-'a'+1;
if(!next[j][k])
next[j][k]=++sz;
j=next[j][k];
}
cnt[j]=1;
}
int topos()
{
int popnow;
int i,j,k;
int h=1,t=0;
for(i=1;i<27;++i)
if(!indegree[i])
que[++t]=i;
while(h<t+1)
{
popnow=que[h];
for(i=head[popnow];i;i=adj[i])
if(indegree[v[i]])
{
--indegree[v[i]];
if(!indegree[v[i]])
que[++t]=v[i];
}
++h;
}
for(i=1;i<27;++i)
if(indegree[i])
return 0;
return 1;
}
int main()
{
int N;
int res[30057];
int i,j,k,o,p,l,ii;
scanf("%d",&N);
i=0;
do
{
++i;
scanf("%s",s[i]);
tinsert(s[i]);
}while(--N);
l=0;
for(p=i,i=1;i<p+1;++i)
{
j=0;
for(ii=0;s[i][ii];++ii)
{
k=s[i][ii]-'a'+1;
if(cnt[j])
break;
for(o=1;o<27;++o)
if(o!=k&&next[j][o])
{
v[++ec]=o;
adj[ec]=head[k];
head[k]=ec;
++indegree[o];
}
j=next[j][k];
}
if(!s[i][ii])
if(topos())
res[++l]=i;
for(j=0;j<ec+2;++j)
adj[j]=0;
for(j=0;j<28;++j)
{
indegree[j]=0;
head[j]=0;
}
ec=0;
}
printf("%d\n",l);
for(i=1;i<l+1;++i)
printf("%s\n",s[res[i]]);
return 0;
}
允许更改小写字母的字典序,要求找出在可以更改字典序的条件下,字典序第一的模式串(小写模式串)。(考虑模式串 abc 和 abd ,在常规字典序 abcde 的情况下字典序第一的模式串是abc ,现在更改常规字典序为 abdce ,这时 abd 成为了字典序第一的模式串。)
考虑无法使模式串成为字典序第一的模式串的 2 种情况,第一( trie树),存在其他模式串是当前模式串的真前缀,第二(拓扑排序),存在其他模式串和当前模式串有公共前缀,公共前缀的后 1 个字母,其他模式串的要比当前模式串的字典序靠前(考虑模式串 moo 、 mom 和 oc ,我想 moo 成为字典序第一,公共前缀空的时候有 m<o ,公共前缀为 mo 的时候有 o<m ,无法使 moo 成为字典序第一的模式串。)。
算法《 https://wswmsword.github.io/2019/06/21/%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F/ 》
算法《 https://wswmsword.github.io/2019/06/01/trie%E5%AD%97%E5%85%B8%E6%A0%91/ 》