*题解:CF508D Tanya and Password

题目链接

解析

貌似是典题。

首先我有一个想法是把每两个字符的组合看成一个点,每个给定字符串 \(s\) 也看成是一个点。每个 \(s\) 向其后缀两个字符连边,其前缀向 \(s\) 连边,那么要求的就是一条经过所有 \(s\) 的路径。直接做不好做,不妨把 \(s\) 变成边,这样就变成求欧拉路径了。

时间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define eps 0.0000000001
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1) 
using namespace std;
typedef long long ll;
typedef unsigned ui;
typedef pair<int,int> pii;
const int N = 2e5 + 5, M = 26 + 26 + 10 + 5, P = 450, mod = (int)1e9 + 7, mod2 = 1e9 + 7, b1 = 131;
string s[N];
int ind[M * M],outd[M * M],now[M * M];
vector<pii> g[M * M];
vector<int> p;
void dfs(int x,int pos){
	for(;now[x] < g[x].size();){
		int nx = g[x][now[x]].first,id = g[x][now[x]].second;
		now[x]++;
		dfs(nx,id);
	}
	p.push_back(pos);
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
	int n;
	cin>>n;
	int cnt = 0;
	map<pair<char,char>,int> mp;
	for(int i=1;i<=n;i++){
		cin>>s[i];
		if(!mp.count({s[i][0],s[i][1]})){
			mp[{s[i][0],s[i][1]}] = ++cnt;
		}
		if(!mp.count({s[i][1],s[i][2]})){
			mp[{s[i][1],s[i][2]}] = ++cnt;
		}
		g[mp[{s[i][0],s[i][1]}]].push_back({mp[{s[i][1],s[i][2]}],i});
		ind[mp[{s[i][1],s[i][2]}]]++;
		outd[mp[{s[i][0],s[i][1]}]]++; 
	}
	bool f1 = true;
	int cnt1 = 0,cnt2 = 0,pos = 0;
	for(int i=1;i<=cnt;i++){
		cnt1 += ind[i] == outd[i] + 1;
		cnt2 += outd[i] == ind[i] + 1;
		if(outd[i] == ind[i] + 1){
			pos = i;
		}
		f1 &= ind[i] == outd[i];	
	}
	
	if(f1 || (cnt1 == 1 && cnt2 == 1)){
		if(f1){
			dfs(1,0);
		}else{
			dfs(pos,0);
		}
		if(p.size() != n + 1){
			cout<<"NO";
			return 0;
		}
		cout<<"YES\n";
		string res;
		for(int i=p.size() - 2;i>=0;i--){
			if(res.empty())
				res += s[p[i]];
			else
				res += s[p[i]][2];
		}
		cout<<res;
	}else{
		cout<<"NO";
	}
	return 0;
}
posted @ 2026-08-11 17:58  yutar  阅读(3)  评论(0)    收藏  举报