字符串的转换路径问题

字符串的转换路径问题

题目描述

给定两个字符串,记为start和to,再给定一个字符串列表list,list中一定包含to,list中没有重复的字符串。所有的字符串都是小写的。规定start每次只能改变一个字符,最终的目标是彻底变成to,但是每次变成新字符串必须在list中存在。请返回所有的最短的变换路径(按照字典序最小的顺序输出)。

输入描述:

输入包含多行,第一行包含一个整数 n ( 1 ≤ n ≤ 5000 ) n(1 \leq n \leq 5000) n(1n5000),代表list的中字符串的个数,第二行中包含两个字符串,分别代表start和to。接下来n行,每行一个字符串,代表lis[i](保证字符串长度都为3)。

输出描述:

如果存在转换的路径,请先输出“YES”,然后按照字典序最小的顺序输出所有路径。如果不存在请输出“NO”。

示例1
输入
8
abc cab
cab
acc
cbc
ccc
cac
cbb
aab
abb
输出
YES
abc -> abb -> aab -> cab
abc -> abb -> cbb -> cab
abc -> cbc -> cac -> cab
abc -> cbc -> cbb -> cab

题解:

建图+BFS+DFS。

法一

直接爆搜,对每个字符串,对每一位字符分别使用 ‘a’-‘z’ 替换,若变换后的字符串在 list 中且未被遍历,则继续递归下去。但是,时间复杂度肯定爆炸。。。

法一代码(TLE):
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <algorithm>

using namespace std;

int n;
string st, ed, q;
vector<vector<string>> ret;
vector<string> tmp;
int min_step = 1 << 30;

unordered_map<string, bool> h1, h2;

inline bool cmp( const vector<string>& v1, const vector<string>& v2 ) {
    for ( int i = 0; i < v1.size(); ++i )
        if( v1[i] != v2[i] ) return v1[i] < v2[i];
    return false;
}

void dfs( string s, int step ) {
    if ( step > min_step ) return;
    tmp.push_back( s );
    if ( s == ed ) {
        if ( step < min_step ) {
            min_step = step;
            ret.clear();
            ret.push_back( tmp );
        } else if ( step == min_step ) {
            ret.push_back( tmp );
        }
        tmp.pop_back();
        return;
    }
    for ( int i = s.size() - 1; i >= 0; --i) {
        char ch = s[i];
        for ( int j = 25; j >= 0; --j ) {
            s[i] = j + 'a';
            if ( !h2[s] && h1.count(s) ) {
                h2[s] = true;
                dfs( s, step + 1 );
                h2[s] = false;
            }
            s[i] = ch;
        }
    }
    tmp.pop_back();
}

int main(void) {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n;
    cin >> st >> ed;
    for ( int i = 0; i < n; ++i ) {
        cin >> q;
        h1[q] = true;
    }
    if ( !h1.count( ed ) ) {
        cout << "NO" << endl;
        return 0;
    }
    dfs( st, 0 );
    if ( !ret.size() ) {
        cout << "NO" << endl;
        return 0;
    }
    cout << "YES" << endl;
    sort( ret.begin(), ret.end(), cmp );
    for ( int i = 0; i < ret.size(); ++i ) {
        for ( int j = 0; j < ret[i].size(); ++j ) {
            if ( j ) cout << " -> ";
            cout << ret[i][j];
        }
        cout << endl;
    }
    return 0;
}

法二:

  1. 我们可以提前预处理出 start 和 list 中的每个字符串变换后的可能的下一个字符串,并根据这个建图
  2. BFS 找出 start 到 to 的最短路径(注意:找到最短路径不要提前退出,需要把所有状态都处理掉,后面会用到)
  3. 根据第二步的结果和状态之间的距离关系,DFS 搜索路径(重要:根据第二步的状态可大大减少搜索状态)
  4. 将路径变换成字符串,并按照字典序从小到大排序并输出

法二代码:

#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

unordered_map<string, vector<string> > g;
unordered_map<string, int> dis;
unordered_set<string> cand;
vector<vector<string>> ret;
string st, ed;
int n, mn_step;

void getNexts() {
    string s = st;
    for ( int i = 0; st[i]; ++i ) {
        for ( s[i] = 'a'; s[i] <= 'z'; ++s[i] ) {
            if ( s[i] != st[i] && cand.find( s ) != cand.end() )
                g[st].push_back( s );
        }
        s[i] = st[i];
    }
    for (auto& it : cand ) {
        s = it;
        for ( int i = 0; it[i]; ++i ) {
            for ( s[i] = 'a'; s[i] <= 'z'; ++s[i] ) {
                if ( s[i] != it[i] && cand.find( s ) != cand.end() )
                    g[it].push_back( s );
            }
            s[i] = it[i];
        }
    }
}

int bfs() {
    queue<string> q;
    q.push( st );
    dis[st] = 0;
    unordered_set<string> vis;
    vis.insert( st );
    string s;
    while ( !q.empty() ) {
        s = q.front();
        q.pop();
        for ( auto& it : g[s] ) {
            if ( vis.find(it) != vis.end() ) continue;
            dis[it] = dis[s] + 1;
            vis.insert( it );
            q.push( it );
        }
    }
    return dis[ed];
}

void dfs( vector<string>& ans ) {
    string s = ans.back();
    if ( s == ed ) ret.push_back( ans );
    else {
        for ( auto& it : g[s] ) {
            if ( dis[s] + 1 == dis[it] && 
                 (dis[it] < mn_step || dis[it] == mn_step && it == ed ) ) {
                ans.push_back( it );
                dfs( ans );
                ans.pop_back();
            }
        }
    }
}

int main(void) {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n;
    cin >> st >> ed;
    string s;
    for ( int i = 0; i < n; ++i ) {
        cin >> s;
        cand.insert( s );
    }
    
    if ( cand.find( ed ) == cand.end() ) {
        cout << "NO" << endl;
        return 0;
    }
    
    getNexts();
    
    mn_step = bfs();
    if ( mn_step == 0 ) {
        cout << "NO" << endl;
        return 0;
    }
    
    vector<string> ans, out;
    ans.push_back( st );
    dfs( ans );
    
    cout << "YES" << endl;
    
    for ( auto& path : ret ) {
        s = "";
        for ( int i = 0; i < path.size(); ++i ) {
            if ( i ) s += " -> ";
            s += path[i];
        }
        out.push_back( s );
    }
    sort( out.begin(), out.end() );
    for ( auto& it : out )
        cout << it << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值