P3065 [USACO12DEC]First! G TRIE树+拓扑排序

题意:

给定\(n\)个单词,求在自定义字典序的情况下,多少个单词的字典序最小,并按顺序输出

范围&性质:\(1\le n\le 3\times 10^4\),每个单词长度不超过20

分析:

暴力做法:

可知,对于一个固定的字典序,有且仅有一个单词字典序最小(不考虑完全相同),所以朴素的做法有了,枚举字典序,在TRIE树上找出对应的字符串,复杂度\(O(26!\times20)\)(字典序数目\(\times\)TRIE树深度)

正解:

假定每一个单词都可以作为最小字典序,对于每个单词按照形成的字典序(上面提到字典序和单词一一对应),由字典序小的字母向大的字母连边,若存在环则假设不成立。复杂度为\(O(26*nm)\)

tips:

  1. 判环可以利用拓扑排序,最后若存在度数不为0的点,即存在环
  2. 对于前缀相同的字符串,大的字典序不可能最小,直接判断就好

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const int maxn = 3e5+5;
	int n,cnt=1,sum=0;
	int son[maxn][26],rd[30],ans[maxn];
	char ch[maxn][30];
	bool vis[maxn*10];
	
	struct edge
	{
		int to,nxt;
	}e[905];
	
	int ecnt=0;
	int head[30];
	void add(int u,int v)
	{
		e[++ecnt].to=v;
		e[ecnt].nxt=head[u];
		rd[v]++;
		head[u]=ecnt;
	}
	
	void insert(int x)
	{
		int len=strlen(ch[x]+1),now=1;
		for(int i=1;i<=len;i++)
		{
			int tmp=ch[x][i]-'a';
			if(!son[now][tmp]) son[now][tmp]=++cnt;
			now=son[now][tmp];
		}
		vis[now]=true;
	}
	
	bool check(int x)
	{
		int len=strlen(ch[x]+1),now=1;
		for(int i=1;i<=len;i++)
		{
			int tmp=ch[x][i]-'a';
			if(vis[now]) return false;
			for(int i=0;i<26;i++)
			{
				if(tmp!=i&&son[now][i])
				{
					add(tmp,i);
				}
			}
			now=son[now][tmp];
		}
		return true;
	}
	
	bool topo()
	{
		queue<int> q;
		for(int i=0;i<26;i++)
		{
			if(!rd[i])
			{
				q.push(i);
			}
		}
		while(!q.empty())
        {
        	int u=q.front();q.pop();
        	for(int i=head[u];i;i=e[i].nxt)
        	{
        		int v=e[i].to;
        		if(rd[v])
        		{
        			if(--rd[v]==0)
        			{
        				q.push(v);
					}
				}
			}
		}
		for(int i=0;i<26;i++)
		{
			if(rd[i]) return false;
		}
		return true;
	}
	
	void work()
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%s",ch[i]+1);
			insert(i);
		}
		for(int i=1;i<=n;i++)
		{
			memset(rd,0,sizeof(rd));
			memset(head,0,sizeof(head));
			ecnt=0;
			if(check(i)&&topo())
			{
				ans[++sum]=i;
			}
		}
		printf("%d\n",sum);
		for(int i=1;i<=sum;i++)
		{
			printf("%s\n",ch[ans[i]]+1);
		}
	}
	
}

int main()
{
	zzc::work();
	return 0;
}
posted @ 2020-09-16 09:41  youth518  阅读(151)  评论(0编辑  收藏  举报