Description
给定 n n n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们
Input
第一行一个数表示 n n n
之后 n n n行每行一个字符串表示给定的字符串
( 1 ≤ n ≤ 3 ⋅ 1 0 4 , ∑ ∣ s ∣ ≤ 3 ⋅ 1 0 5 ) (1\le n\le 3\cdot 10^4,\sum|s|\le 3\cdot 10^5) (1≤n≤3⋅104,∑∣s∣≤3⋅105)
Output
第一行输出一个数 x x x表示可行的字符串个数
之后输出 x x x行,每行输出一个可行的字符串
输出的顺序和输入的顺序一致
Sample Input
6
mcfx
ak
ioi
wen
l
a
Sample Output
5
mcfx
ioi
wen
l
a
Solution
对所有串建字典树,如果一个串以另一个串为前缀,那么这个串必然不能是字典序最小的,否则在每个对应位置建立该串字符到其他串字符的偏序关系,以此建图,如果该图存在拓扑序则说明存在一种字符之间的大小关系使得该串字典序最小,否则不行,时间复杂度 O ( 26 ∑ ∣ s ∣ + 2 6 2 n ) O(26\sum|s|+26^2n) O(26∑∣s∣+262n)
Code
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=300005;
string s[30005],ans[30005];
int tree[300005][27],val[300005],tot=0,n;
void insert(string s)
{
int len=s.size(),p=0;
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
if(tree[p][x]==0)tree[p][x]=++tot;
p=tree[p][x];
}
val[p]=1;
}
int du[27];
vector<int>g[27];
bool top_sort()
{
queue<int>que;
int res=0;
for(int i=0;i<26;i++)
if(!du[i])que.push(i),res|=(1<<i);
while(!que.empty())
{
int u=que.front();
que.pop();
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
du[v]--;
if(!du[v])que.push(v),res|=(1<<v);
}
}
return res==((1<<26)-1);
}
bool check(string s)
{
int len=s.size(),p=0;
memset(du,0,sizeof(du));
for(int i=0;i<26;i++)g[i].clear();
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
for(int y=0;y<26;y++)
if(tree[p][y]&&y!=x)
{
du[y]++;
g[x].push_back(y),g[y].push_back(x);
}
if(val[p])return 0;
p=tree[p][x];
}
return top_sort();
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>s[i];
insert(s[i]);
}
int res=0;
for(int i=1;i<=n;i++)
if(check(s[i]))ans[++res]=s[i];
printf("%d\n",res);
for(int i=1;i<=res;i++)cout<<ans[i]<<endl;
return 0;
}