牛客练习赛11 B (字典树+拓扑排序)

题意

给定n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们

做法

每一个串如果有一个串是它的前缀,则肯定不行
否则每次从这个字母向同一个父亲的其他字母连边
可以通过拓扑排序找环

这题空间很紧啊 服了

#include <bits/stdc++.h>
using namespace  std;
//#define  int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
#define fi first
#define se second
#define pb  push_back
#define inf 1<<62
#define endl "\n"
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define de_bug(x) cerr << #x << "=" << x << endl
#define all(a) a.begin(),a.end()
#define IOS   std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define  fer(i,a,b)  for(int i=a;i<=b;i++)
#define  der(i,a,b)  for(int i=a;i>=b;i--)
const int mod = 1e9 + 7;
const int N = 3e5 + 10;
int n, m , k;
vi g[33];
int d[33];
int son[N][26];
int idx;
int cnt[N];
void insert(string s) {
	int p = 0;
	for(int i = 0; i < (int)s.size(); i++) {
		int u = s[i] - 'a';
		if(!son[p][u])son[p][u] = ++idx;
		p = son[p][u];
	}
	cnt[p]++;
}
bool check(string s ) {
	int p = 0;
	for(int i = 0; i < (int)s.size(); i++) {
		int u = s[i] - 'a';
		if(cnt[p])return false;
		for(int k = 0; k < 26; k++) {
			if(son[p][k] && k != u) {
				g[u].push_back(k);
				d[k]++;
			}
		}
		p = son[p][u];
	}
	return true;
}
bool to(string s) {
	queue<int>q;
	int cnt = 0;
	for(int i = 0; i < 26; i++) {
		if(!d[i])q.push(i) ;
	}
	while(!q.empty()) {
		int u = q.front();
		q.pop();
        cnt++;
		for(auto v : g[u]) {
			if(!--d[v])q.push(v);
		}
	}
	//cout<<cnt<<endl;
	return cnt == 26;
}
string a[30010];
vector<string>ans;
int cnt1;
void solve() {
	cin >> n;
	fer(i, 1, n)cin >> a[i], insert(a[i]);
	fer(i, 1, n) {
		fer(j, 0, 30) {
			g[j].clear();
			d[j] = 0;
		}
		if(  check(a[i]) &&to(a[i]) ) ans.push_back(a[i]);
	}
	cout << ans.size() << endl;
	for(auto v : ans) {
		cout << v << "\n "[v == ans.back()];
	}
}
int main() {
	IOS;
	int _ = 1;
	//cin>>_;
	while( _-- )
		solve();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值