D167 欧拉路径 P1127 词链
D167 欧拉路径 P1127 词链_哔哩哔哩_bilibili
给 n 个小写字母的单词,输出字典序最小的词链,使得每个单词在词链中出现且仅出现一次
思路
单词之间通过字母连接,我们就将字母看作点(最多 26 个),单词看做边
从单词的首字母向尾字母连有向边,比如单词 aob,就从 a 向 b 连有向边
建图后,先判断是否存在欧拉路,再 DFS 寻找字典序最小的欧拉路
注意:本题的字典序应该按单词的字典序来排序,而非结点的字典序
如图,按单词的字典序用邻接表存图,找到字典序最小的词链为 aae.ec.cb.ba.abc.cca.aob

参考:D165【模板】有向图 欧拉路径 欧拉回路 P7771 欧拉路径 - 董晓 - 博客园
// 欧拉路径 O(nlogn) #include<bits/stdc++.h> using namespace std; int n,cd[26],rd[26]; vector<int> path; vector<string> wd; //单词 vector<vector<pair<int,int>>> e(26); //邻接表 int p[26]; //p[x]表示点x当前处理到第几条出边,初始值为0,相当于全局指针 void dfs(int x,int id){ //x点,x点的入边编号 for(int i=p[x]; i<e[x].size(); i=p[x]){ p[x]=i+1; dfs(e[x][i].first, e[x][i].second); } if(id!=-1) path.push_back(id); //记录入边编号 } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n; for(int i=0; i<n; i++){ string s; cin>>s; wd.emplace_back(s); } sort(wd.begin(),wd.end()); //单词升序 for(int i=0; i<n; i++){ int u=wd[i][0]-'a'; int v=wd[i][wd[i].size()-1]-'a'; e[u].push_back({v,i}); //i表示单词序 cd[u]++; rd[v]++; } for(int i=0; i<26; i++)if(!e[i].empty()) sort(e[i].begin(),e[i].end(), //对每个字母的出边按单词序排序 [&](pair<int,int>& a,pair<int,int>& b){return a.second<b.second;}); int cs=0,rs=0; bool ok=true; //存在欧拉路 for(int i=0; i<26; i++){ if(rd[i]!=cd[i]){ if(cd[i]-rd[i]==1) cs++; //出度多1的点数 else if(rd[i]-cd[i]==1) rs++; //入度多1的点数 else ok=false; //度差>1,一定不存在欧拉路 } } if(!(!cs&&!rs || cs==1&&rs==1)) ok=false; //非环非路一定不存在 int start=0; while(!cd[start]) ++start; //找环的起点 for(int i=0; i<26; i++) if(cd[i]-rd[i]==1){start=i; break;} //找路的起点 dfs(start,-1); //搜索欧拉路 if(path.size()!=n) ok=false; //如果不连通 if(!ok) return cout<<"***\n",0; for(int i=path.size()-1; i>=0; i--){ cout<<wd[path[i]]; if(i) cout<<'.'; } return 0; }
浙公网安备 33010602011771号