【庞果英雄会】最小操作数

来源:互联网 发布:为知笔记怎么分享 编辑:程序博客网 时间:2024/04/30 14:36

给了A、B两个单词和一个单词集合Dict,每个的长度都相同。我们希望通过若干次操作把单词A变成单词B,每次操作可以改变单词中的一个字母,同时,新产生的单词必须是在给定的单词集合Dict中。求所有行得通步数最少的修改方法。    

举个例子如下: 

Given:    A = "hit"    B = "cog"    

Dict = ["hot","dot","dog","lot","log"] 

Return  [    ["hit","hot","dot","dog","cog"],    ["hit","hot","lot","log","cog"]  ]     

即把字符串A = "hit"转变成字符串B = "cog",有以下两种可能: 

"hit" -> "hot" ->  "dot" ->  "dog" -> "cog"; 

"hit" ->  "hot" ->  "lot" ->  "log"  ->"cog"。 


我的思路如下:

广度优先方式建立多叉树(类似多叉树)。从开始给的字符开始,从字典中找相差一个字母的所有集合,都为开始字符的孩子节点,并将这些节点放到一个列表list中。然后对列表list中的每一个节点N,继续找相差一个字母的所有集合(不包含已经在树上,且深度低于当前处理层的节点),做为N的孩子节点。这样,以广度优先的方式来建立多叉树。最后遇到了单词B,把这一层处理完,就可以从树上倒推得到变化过程了(可行的变化可能有多个)。

根据例子中所给的字典建立多叉树的过程(先要将字符B加入到字典中):


需要注意的是:

1 树是逐层建立起来的,也就是广度优先,而不是深度优先,例如第3层有dot和lot,先将dot和lot放到树上,再着手建立第4层。所以,需要一个辅助列表来保存每一层的的节点。以便于建立下一层。

2 为了保证树的建立过程中,不会出现倒回头的现象,所以已经添加到树上的节点需要标记,在我的代码中初始化节点的深度为-1,表示还未添加到树上。

3 树的两个分支可能包含同一个孩子节点,例如上面从第4层到第5层。cog节点有两个父节点,所以严格来说,这并不是一个树^_^ 另外,cog在处理dog节点的时候已经添加到树上,但是在处理log节点时,又处理了一次,看似于第2点矛盾,其实不然,因为dog和log是处在同一层的,为了记录所有可行的方案需要这样处理。

4 可以不记录节点的孩子节点情况,但是为了最后回溯结果,每个节点要记录自己的父节点(可能有多个父节点)。

5 回溯就比较简单了,用一个数组模拟栈,从节点B不停地往父节点方向走,记录所有可行路径。


具体还是看代码吧,我下面这个代码在庞果网提交并没有通过,应该还是有考虑不周的地方,恳请读者不吝赐教。

// MinOperationCount.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <string>#include <vector>#include <map>#include <set>#include <iostream>using namespace std;typedef struct _NODE{    vector<string> vecParentNode;    int nDeep;    bool isEnd;}NODE, *PNODE;bool IsNear(const string & strDest, const string & strSource){    int nLenDest    = strDest.length();    int nLenSource  = strSource.length();    if (nLenSource != nLenDest)    {        return false;    }    int nDiff = 0;    for (int i = 0; i < nLenSource; i++)    {        if (strDest.at(i) != strSource.at(i))        {            nDiff++;        }    }    if (1 == nDiff)    {        return true;    }    return false;}bool FindNearString(set<string> &setStrFind, map<string, NODE> &mapStrNode, int nDeep){    bool bRet = false;    set<string> setStrNext;    set<string>::iterator itSet = setStrFind.begin();    for ( ; itSet != setStrFind.end(); itSet++)    {        map<string, NODE>::iterator it = mapStrNode.begin();        for ( ; it != mapStrNode.end(); it++)        {            if ( (-1 == it->second.nDeep || nDeep == it->second.nDeep) &&                  IsNear(*itSet, it->first))            {                it->second.nDeep = nDeep;                it->second.vecParentNode.push_back(*itSet);                setStrNext.insert(it->first);                if (it->second.isEnd)                {                    bRet = true;                }            }        }    }    setStrFind = setStrNext;    return bRet;}void OutputArray(string* arrayPath, int nDeep, vector<vector<string>> &vecResult){    vector<string> vecStr;    for (int i = nDeep-1; i >= 0; i--)    {        vecStr.push_back(arrayPath[i]);    }    vecResult.push_back(vecStr);}void Recur(string* arrayPath, int i, int nDeep, string &strCur,            map<string, NODE> &mapStrNode, vector<vector<string>> &vecResult){    arrayPath[i] = strCur;    map<string, NODE>::iterator it = mapStrNode.find(strCur);    if (it == mapStrNode.end())    {        OutputArray(arrayPath, nDeep, vecResult);        return;    }    for (int j = 0; j < (int)mapStrNode[strCur].vecParentNode.size(); j++)    {        i++;        Recur(arrayPath, i, nDeep, mapStrNode[strCur].vecParentNode[j], mapStrNode, vecResult);        i--;    }}void GetMinPath(string &strBegin, string &strEnd,                 set<string> &setDict, vector<vector<string>> &vecResult){    if (strBegin == strEnd)    {        return;    }    map<string, NODE> mapStrNode;    setDict.insert(strEnd);    set<string>::iterator it = setDict.begin();    for ( ; it != setDict.end(); it++)    {        NODE node = {};        node.nDeep = -1;// -1 代表还没有插入到树上        node.isEnd = (*it == strEnd) ? true : false;        mapStrNode[*it] = node;    }    //广度优先建立多叉树    set<string> setStrFind;    setStrFind.insert(strBegin);    int nDeep = 1;    bool isFind = false;    while (setStrFind.size() != 0 && !isFind)    {        isFind = FindNearString(setStrFind, mapStrNode, nDeep);        nDeep++;    }    if (!isFind)    {        return;    }    string* arrayPath = new string[nDeep];    int i = 0;    Recur(arrayPath, i, nDeep, strEnd, mapStrNode, vecResult);    delete [] arrayPath;}class Solution{public:    vector<vector<string>> findLadders(string start, string end, set<string>& dict)    {        vector<vector<string>> vecResult;        GetMinPath(start, end, dict, vecResult);        return vecResult;    }};int _tmain(int argc, _TCHAR* argv[]){    string strBegin = "hit";    string strEnd= "cig";    set<string> setDict;    setDict.insert("hot");    setDict.insert("git");    setDict.insert("dot");    setDict.insert("dog");    setDict.insert("lot");    setDict.insert("log");    setDict.insert("cog");    vector<vector<string>> vecResult;    Solution so;    vecResult = so.findLadders(strBegin, strEnd, setDict);    vector<vector<string>>::iterator it = vecResult.begin();    for ( ; it != vecResult.end(); it++)    {        vector<string>::iterator itStr = it->begin();        for ( ; itStr != it->end(); itStr++)        {            cout<<*itStr<<" ";        }        cout<<"\n";    }return 0;}

update:

经过特例测试,发现是没有考虑到开始字符串出现在字典中的情况,所以将85行代码:

if (it == mapStrNode.end()) 

修改为:

    if (it == mapStrNode.end() || nDeep == i+1)  即可。


原创粉丝点击