zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Apache Pig使用MongoLoader产生大量空文件问题
    执行sparksql出现OOM问题
    [TD笔记]Teradata XML
    Redhat上离线/非root安装python库
    Java 多线程同步关键字synchronized各种用法/特性 总结
    CVE-2019-0708 微软远程桌面服务远程代码执行漏洞分析之补丁分析
    Spring Security 实战(使用Spring Boot项目演示)
    一些渗透中,或者扫描的nmap nse脚本推荐
    kali学习笔记(一):虚拟机安装好kali后应进行的配置
    Spring boot相关问题
  • 原文地址:https://www.cnblogs.com/youth518/p/13677377.html
Copyright © 2011-2022 走看看