D167 欧拉路径 P1127 词链

D167 欧拉路径 P1127 词链_哔哩哔哩_bilibili

 

P1127 词链 - 洛谷

给 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;
}
 

 

posted @ 2026-05-26 16:36  董晓  阅读(60)  评论(0)    收藏  举报