suxxsfe

一言(ヒトコト)

P3065 [USACO12DEC]First! G

https://www.luogu.com.cn/problem/P3065
历史遗留题目,在收藏里吃灰好长时间了,觉得洛谷可以整一个记录加入收藏的时间的功能,让我看看每个题咕了多长时间
然后今天看突然有些会了

给定 \(n\) 个字符串,可以指定字母之间的大小关系(比如可以指定 \(b<a\),其实也就是指定了一种字母表顺序),问哪些串可以在任意一种字母表顺序下字典序最小
trie+建图判环


先把输入的字符串都放到trie上
对于每一个字符串 \(s\),设当前考虑到 \(s_i\)(也就是在trie上走到了 \(s_i\) 的上一层,要往 \(s_i\) 走)
那么就假设 \(s_i\)\(s_i\) 所在层所有存在的字符中最大的,如何把这个“最大”表达出来?
就拿他向其它字符都连边(单项,大的连向小的),然后直到考虑完 \(s_n\),如果连出的图没有环,则说明成立,否则,说明无论什么字母表下,\(s\) 的字典序都不是最小
至于判环就爱咋判咋判了,我这里用的拓扑排序

还有一点是如果发现某一个字符串是 \(s\) 的前缀,也说明 \(s\) 的字典序不可能最小

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
struct tr{
	tr *son[26];
	int end;
}dizhi[300005],*root=&dizhi[0];
int tot,n;
int map[26][26],in[26];
char *begin[30005];
char s[300005],in_str[300005];
int tail,head,queue[105];
int len[30005],yes[30005];
inline void topo(){
	tail=-1;head=0;
	for(reg int i=0;i<26;i++)if(!in[i]) queue[++tail]=i;
	reg int u,v;
	while(head<=tail){
		u=queue[head++];
		for(v=0;v<26;v++)if(map[u][v])
			if(!--in[v]) queue[++tail]=v;
	}
}
inline int check(int id,char *s){
	tr *now=root;
	int num;
	std::memset(map,0,sizeof map);std::memset(in,0,sizeof in);
	for(reg int i=0;i<len[id];i++){
		if(now->end) return 0;
		num=s[i]-'a';
		for(reg int j=0;j<26;j++)if(now->son[j]&&num!=j&&!map[num][j]){
			map[num][j]=1;in[j]++;
		}
		now=now->son[num];
	}
	topo();
	for(reg int i=0;i<26;i++)if(in[i]) return 0;
	return 1;
}
int main(){
	n=read();
	for(reg int i=1;i<=n;i++){
		scanf("%s",in_str);
		len[i]=std::strlen(in_str);
		tr *now=root;
		for(reg int j=0;j<len[i];j++){
			if(!now->son[in_str[j]-'a']) now->son[in_str[j]-'a']=&dizhi[++tot];
			now=now->son[in_str[j]-'a'];
		}
		now->end=1;
		int tmp=std::strlen(s);
		begin[i]=&s[tmp];
		for(reg int j=0;j<len[i];j++) s[j+tmp]=in_str[j];
	}
	int ans=0;
	for(reg int i=1;i<=n;i++)if(check(i,begin[i])) yes[i]=1,ans++;
	printf("%d\n",ans);
	for(reg int i=1;i<=n;i++)if(yes[i]){
		char *now=begin[i];
		for(reg int j=0;j<len[i];j++) putchar(*now),now++;
		EN;
	}
	return 0;
}
posted @ 2020-07-21 16:20  suxxsfe  阅读(89)  评论(0编辑  收藏  举报