First!(ZJNU 2832)

题目大意

你有n个字符串,现在你可以改变字典序的规则(改变字母间大小关系),使得某字符串成为字典序最小的字符串,问字符串S1,S2,,Sn中哪些可以成为字典序最小的字符串。(1n30000)

思路

由于我们想让某字符串字典序最小,那么就一定能得到字母与字母间的大小关系,而且如果这些关系没有冲突,那么就说明是能满足字典序最小的。对于这样的关系,我们可以用单向边来表示,然后冲突就说明有环,可以用拓扑或DFS来处理。但是由于n的范围,我们不可能对所有的字符串两两比较,于是我们考虑用trie树。我们可以先把所有的字符串都插到trie树中,然后再拿每一个字符串放到trie树中比较,就能很快得到字母间的关系,然后通过拓扑就能轻松解决。

代码

#include<bits/stdc++.h>
using namespace std;
int trie[500005][30];
int ed[500005];
int k=1;
int edge[30][30];
void insert(string w)
{
	int len=(int)w.size();
	int p=0;
	for(int i=0;i<len;i++)
	{
		int c=w[i]-'a';
		if(!trie[p][c])
			trie[p][c]=k++;
		p=trie[p][c];
	}
	ed[p]=1;
}
int search(string s)
{
	int len=(int)s.size();
	int p=0;
	int in[30]={0};
	int cc=26;
	for(int i=0;i<len;i++)
	{
		if(ed[p])return 0;
		int c=s[i]-'a';
		for(int j=0;j<26;j++)if(j!=c&&trie[p][j])
		{
			if(!edge[c][j])in[j]++;
			edge[c][j]=1;
		}
		p=trie[p][c];
	}
	queue<int>q;
	for(int i=0;i<26;i++)if(in[i]==0)q.push(i);
	while(!q.empty())
	{
		int d=q.front();
		cc--;
		q.pop();
		for(int i=0;i<26;i++)
		{
			if(edge[d][i])
			{
				in[i]--;
				if(in[i]==0)q.push(i);
			}
		}
	}
	if(cc)return 0;
	return 1;
}
string s[50005];
int cnt=0;
int vis[50005];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		insert(s[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<26;j++)
			for(int kd=0;kd<26;kd++)
				edge[j][kd]=0;
		vis[i]=search(s[i]);
		if(vis[i])cnt++;
	}
	printf("%d\n",cnt);
	for(int i=1;i<=n;i++)
		if(vis[i])cout<<s[i]<<endl;
	return 0;
}

__EOF__

本文作者Jerry-Black
本文链接https://www.cnblogs.com/Jerry-Black/p/16459658.html
关于博主:小蒟蒻一只( ̄^ ̄)ゞ
版权声明:转载请注明来源哟~ QAQ
声援博主:UP UP UP !!!
posted @   Jerry_Black  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示