题目
知识点
思路
直接广度优先搜索
虽然和字符串有关,本质其实就是最短路径问题。因此,使用BFS做肯定是没有问题的。 简单思路:每一次把所有“到达”的单词放入队列中(符合“到达”的条件:单词只有一个字母不相等),每次看队头单词是否 endWord
相同,如果相同说明找到了“最段路径”。这里我使用了一个结构体记录状态,里面的step就是当前状态需要经过的变化的次数 问题: 数据给的比较大!在最后一组测试数据过不去(怎么改都过不去),问题应该是找可以“到达”时候的效率太慢(从a到z查询替换没有试,或许可以QAQ),另外就是,生成的广度优先遍历树也太大了。关键代码如下 :(最后一组时间超时 49 / 49 个通过测试用例,判重函数ok()可以在双向BFS代码中查看) int bfs ( vector< string> & wordList) {
q. push ( path ( bg, 1 ) ) ;
while ( ! q. empty ( ) ) {
path u = q. front ( ) ;
q. pop ( ) ;
if ( u. s == ed) return u. step;
for ( const string& i : wordList) {
if ( ok ( i, u. s) && ! vis. count ( i) ) {
q. push ( path ( i, u. step + 1 ) ) ;
vis. insert ( i) ;
}
}
}
return 0 ;
}
双向广度优先搜索
好家伙,超时真的整的头大,只好双向BFS了。 双向BFS画个图进行一下直观的解释:
这是直接广度优先搜索示意图(老灵魂画手 ): 这是双向广度优先搜索示意图 双向BFS相比于直接BFS,同时从Begin和End出发进行BFS,直到相遇元素,最短路径就是这个元素从Begin的路径+从End到这个元素的路径。相比于直接BFS,搜索域小了很多(图中可能画的不是特别明显),因此可以提高效率。 同时使用两个队列 queue<path> q_front, q_end
,使用两个 unordered_set<string> vis_front, vis_end
用于记录两个方向的队列中的元素是否访问过。如果某个队列访问到该元素恰好在反方向也已经遍历过,那么结果就是从正向和从逆向到该元素路径距离之和即使最短“路径”(Line 31-38 ,Line 59-58 )。 PS:由于队列没法直接查找元素,因此需要在反方向的队列中,一个个遍历去访问找到这个节点。
代码
class Solution {
public :
struct path {
string s;
int step;
path ( string ss, int sp) : s ( ss) , step ( sp) { }
} ;
string ed;
queue< path> q_front, q_end;
unordered_set< string> vis_front, vis_end;
bool ok ( string after, string before) {
int cnt = 0 ;
for ( int i = 0 ; i < ed. size ( ) ; i++ ) {
if ( after[ i] != before[ i] && ++ cnt > 1 ) {
return false ;
}
}
return cnt == 1 ;
}
int bfs ( string & bg, vector< string> & wordList) {
while ( ! q_front. empty ( ) || ! q_end. empty ( ) ) {
if ( ! q_front. empty ( ) ) {
path u = q_front. front ( ) ;
q_front. pop ( ) ;
for ( const string & i : wordList) {
if ( ok ( i, u. s) && ! vis_front. count ( i) ) {
if ( vis_end. count ( i) ) {
int end_step;
while ( ! q_end. empty ( ) ) {
path v = q_end. front ( ) ;
q_end. pop ( ) ;
if ( v. s == i) end_step = v. step;
}
return u. step + end_step;
}
q_front. emplace ( path ( i, u. step + 1 ) ) ;
vis_front. insert ( i) ;
}
}
}
if ( ! q_end. empty ( ) ) {
path u = q_end. front ( ) ;
q_end. pop ( ) ;
for ( const string & i : wordList) {
if ( ok ( i, u. s) && ! vis_end. count ( i) ) {
if ( vis_front. count ( i) ) {
int front_step;
while ( ! q_front. empty ( ) ) {
path v = q_front. front ( ) ;
q_front. pop ( ) ;
if ( v. s == i) front_step = v. step;
}
return u. step + front_step;
}
q_end. emplace ( path ( i, u. step + 1 ) ) ;
vis_end. insert ( i) ;
}
}
}
}
return 0 ;
}
int ladderLength ( string beginWord, string endWord, vector< string> & wordList) {
ed = endWord;
if ( find ( wordList. begin ( ) , wordList. end ( ) , ed) - wordList. begin ( ) == wordList. size ( ) ) return 0 ;
q_front. emplace ( path ( beginWord, 1 ) ) , q_end. emplace ( path ( endWord, 1 ) ) ;
vis_front. emplace ( beginWord) , vis_end. emplace ( endWord) ;
return bfs ( beginWord, wordList) ;
}
} ;