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

16 篇文章 0 订阅

给了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)  即可。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值