字符串的转换路径问题
题目描述
给定两个字符串,记为start和to,再给定一个字符串列表list,list中一定包含to,list中没有重复的字符串。所有的字符串都是小写的。规定start每次只能改变一个字符,最终的目标是彻底变成to,但是每次变成新字符串必须在list中存在。请返回所有的最短的变换路径(按照字典序最小的顺序输出)。
输入描述:
输入包含多行,第一行包含一个整数 n ( 1 ≤ n ≤ 5000 ) n(1 \leq n \leq 5000) n(1≤n≤5000),代表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;
}
法二:
- 我们可以提前预处理出 start 和 list 中的每个字符串变换后的可能的下一个字符串,并根据这个建图
- BFS 找出 start 到 to 的最短路径(注意:找到最短路径不要提前退出,需要把所有状态都处理掉,后面会用到)
- 根据第二步的结果和状态之间的距离关系,DFS 搜索路径(重要:根据第二步的状态可大大减少搜索状态)
- 将路径变换成字符串,并按照字典序从小到大排序并输出
法二代码:
#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;
}