题目
给定一个n(n<=3e4),以下n个均为小写字母的字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),
求有多少个串可能成为字典序最小的串,并输出它们,字符串总长≤3e5
思路来源
某篇 2015年的集训队论文 例题
题解
①如果A串是B串的前缀,则B串一定不是字典序最小的
②否则,判断B串的每一位y,在每一位的同一层的字母x只能字典序比y大
y->所有x连边,建图,拓扑判断是否有环,无环则说明可行,否则不可行
由于边实际上可能较多,为了防若干不必要重边,还是建邻接矩阵吧
复杂度O(26*字符串总长+26*26*n)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e4+10,M=3e5+10;
string s[N];
int tr[M][26],num[M],cnt;
int n,rt,res;
int in[26];
bool ok[N],mp[26][26];
queue<int>q;
bool vis[26];
bool topo()
{
for(int i=0;i<26;++i)
{
for(int j=0;j<26;++j)
{
if(mp[i][j])in[j]++;
}
}
for(int i=0;i<26;++i)
{
if(in[i]==0)
{
q.push(i);
vis[i]=1;
}
}
while(!q.empty())
{
int t=q.front();
q.pop();
for(int j=0;j<26;++j)
{
if(!mp[t][j])continue;
mp[t][j]=0;
if((--in[j])==0)
{
q.push(j);
vis[j]=1;
}
}
}
for(int i=0;i<26;++i)
if(!vis[i])return 1;
return 0;
}
void add(int rt,string &s,int id)
{
int len=s.size();
for(int i=0;i<len;++i)
{
int v=s[i]-'a';
if(!tr[rt][v])
{
tr[rt][v]=++cnt;
num[cnt]=0;
memset(tr[cnt],0,sizeof tr[cnt]);
}
rt=tr[rt][v];
}
num[rt]=id;
}
bool solve(int id)
{
for(int i=0;i<26;++i)
{
in[i]=vis[i]=0;
for(int j=0;j<26;++j)
mp[i][j]=0;
}
int rt=0;
int len=s[id].size();
for(int i=0;i<len;++i)
{
int v=s[id][i]-'a';
if(num[rt])return 0;
for(int j=0;j<26;++j)
{
if(j==v||!tr[rt][j])continue;
mp[v][j]=1;
}
rt=tr[rt][v];
}
if(topo())return 0;
return 1;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
rt=cnt=0;
num[0]=0;
memset(tr[0],0,sizeof tr[0]);
for(int i=1;i<=n;++i)
{
cin>>s[i];
add(rt,s[i],i);
}
for(int i=1;i<=n;++i)
if(solve(i))ok[i]=1,res++;
cout<<res<<endl;
for(int i=1;i<=n;++i)
if(ok[i])cout<<s[i]<<endl;
return 0;
}