zoukankan      html  css  js  c++  java
  • 单源无权最短路径举例——字梯问题

      1/*
      2最短路径的一个举例:
      3
      4问题描述:在字梯游戏中,每一个词语都是通过将字梯的前一个词改变一个字母形成的。
      5例如,我们可以通过一系列的单字母替换将zero转换成five:zero, hero, here, hire, fire, five。
      6
      7这个是一个无权最短路径问题,每个词语是个顶点,如果两个顶点可以通过一个字母的替换相互转换的话,
      8这两个顶点就有一个双向的边。
      9
     10首先,我们要从一个字典中生成这么一个图:使用STL中的map,其中键是单词(string),
     11值是一个通过此单词一次变换能够得到的单词的集合vector<string>。
     12问题的关键在于:如何从一个包含89000个单词的词组来构造map。
     13可以用蛮力法,但是时间太慢,所有单词之间都要比较。一个优化是:避免比较长度不同的单词。
     14这个可以通过将单词按照长度先分组来做到。
     15
     16一个方法是:对每个相同长度的数组用蛮力法。
     17另一个方法是:附加一个map,其中的键是一个反映单词长度的int,值是所有的具有这个长度的单词集合。
     18思想还是同上。
     19第三个方法是:使用附加map。将单词按照长度分组,对每个组分别操作。例如对长度为4的小组。
     20对每个长度为4的单词删除第一个字母,保留剩下的三个字母的样本,生成一个map,其中的键是这个样本,
     21值是所有的有这个样本单词的vector。例如单词"wine",去掉'w'生成样本"ine",则样本"ine"对应的
     22vector是"dine", "fine", "wine", "nine".,样本"oot"对应:boot, foot, hoot, loot..最后的这个map的值(vector)就是
     23一组单词,其中每个单词都能够通过一个字符的替换生成其他的单词。
     24*/

     25
     26#include <iostream>
     27#include <sstream>
     28#include <algorithm>
     29#include <string>
     30#include <map>
     31#include <set>
     32#include <list>
     33#include <stack>
     34#include <queue>
     35#include <cctype>
     36#include <vector>
     37#include <bitset>
     38#include <cmath>
     39//#include <hash_map>
     40
     41#define FORR(i, a, b) for(int i = a; i < b; ++i)
     42#define BE(x) x.begin(),x.end()
     43#define MP(a, b) make_pair(a, b)
     44
     45using namespace std;
     46//using namespace stdex;
     47
     48typedef vector<string> VS;
     49typedef vector<int> VI;
     50
     51//words是所有的单词
     52map<string, VS> computerAdjacentWords(const VS& words)
     53{
     54    map<string, VS> adjWords;
     55    map<int, VS> wordsByLength;
     56
     57    //按长度分组
     58    for (int i = 0; i < words.size(); i++)
     59    {
     60        wordsByLength[words[i].length()].push_back(words[i]);
     61    }

     62    map<int, VS>::const_iterator itr;
     63    //对每个分组操作
     64    for (itr = wordsByLength.begin(); itr != wordsByLength.end(); ++itr)
     65    {
     66        const VS& groupsWords = itr->second;
     67        int groupNum = itr->first;
     68
     69        //操作每个组中的每个位置
     70        for (int i = 0; i < groupNum; i++)
     71        {
     72            //形如:"ine" -> "fine", "wine", "nine".
     73            map<string, VS> repToWord;
     74
     75            for (int j = 0; j < groupsWords.size(); j++)
     76            {
     77                string rep = groupsWords[j];
     78                rep.erase(i, 1);
     79                //map的无重复性就是好,而且如果没有对应的可以直接新建一个
     80                //wine加到ine中,fine也加到ine中
     81                //wine也加到wne中
     82                repToWord[rep].push_back(groupsWords[j]);
     83            }

     84
     85            // 然后查找map中的值不止一个单词的(就是有像wine,fine这种)
     86            map<string, VS>::const_iterator itr2;
     87            for (itr2 = repToWord.begin(); itr2 != repToWord.end(); ++itr2)
     88            {
     89                const VS& clique = itr2->second;
     90                if (clique.size() >= 2)
     91                {
     92                    for (int p = 0; p < clique.size(); p++)
     93                    {
     94                        for (int q = p + 1; q < clique.size(); q++)
     95                        {
     96                            adjWords[clique[p]].push_back(clique[q]);
     97                            adjWords[clique[q]].push_back(clique[p]);
     98                        }

     99                    }

    100                }

    101            }

    102        }

    103    }

    104    return adjWords;
    105}

    106
    107/*
    108下面就要使用单元无权最短路径算法了:
    109上面生成的map就相当是一个邻接链表的图表示:
    110一个节点和所有与它相连的节点集合。
    111由于,单源无权最短路径算法只能给出每个节点在路径中的前驱节点,所以,
    112这里返回的map<string,string>值就是最短路径中每个节点的相应前驱节点,而map中所有的键就是
    113最短路径中的所有节点。例如:将zero转换成five:zero, hero, here, hire, fire, five,则,
    114findChain返回的map<string,string>previousWord就是previousWord[five] = fire, previousWord[here] = hero。
    115注意:这里是找到从zero出发到所有节点的路径(中节点的前驱集合,所以没有second参数)
    116*/

    117map<stringstring> findChain(const map<string, VS> & adjacentWords, const string& first)
    118{
    119    map<stringstring> previousWord;
    120    queue<string> q;
    121
    122    q.push(first);
    123    while (!q.empty())
    124    {
    125        string current = q.front();
    126        q.pop();
    127
    128        map<string, VS>::const_iterator itr;
    129        itr = adjacentWords.find(current);
    130
    131        const VS& adj = itr->second;
    132        for (int i =0; i < adj.size(); i++)
    133        {
    134            if (previousWord[adj[i]] == "")
    135            {
    136                previousWord[adj[i]] = current;
    137                q.push(adj[i]);
    138            }

    139        }

    140    }

    141    previousWord[first] = "";
    142
    143    return previousWord;
    144}

    145
    146VS getChainFromPrevMap(const map<stringstring>& previous, const string& second)
    147{
    148    VS result;
    149    //类型转换,因为操作符[]不能用在不可变的map中
    150    map<stringstring>& prev = const_cast<map<stringstring> &> (previous);
    151
    152    for (string current = second; current != ""; current = prev[current])
    153        result.push_back(current);
    154
    155    reverse(result.begin(), result.end());
    156
    157    return result;
    158}

    159
    160int main()
    161{
    162    string a[] = {"five""hero""boyfriend""james""zero""good""hire""thank""fire"
    163        "here""pen""ccbb""greatman""come""great""greet""gold""glad"}
    ;
    164    VS input(a, a+18);
    165    string first("zero"), second("five");
    166
    167    //先构造一个图
    168    map<string, VS> adjWords = computerAdjacentWords(input);
    169
    170    //再构造某个节点到所有节点最短路径中先驱集合
    171    map<stringstring> previousWords = findChain(adjWords, first);
    172
    173    //在找出到某个终点的最短路径
    174    VS result = getChainFromPrevMap(previousWords, second);
    175
    176    FORR(i, 0, result.size())
    177    {
    178        cout << result[i] << " ";
    179    }

    180    cout << endl;
    181
    182    return 0;
    183}
  • 相关阅读:
    机器学习:简介
    对API的理解
    软件的运行
    大数据:数据库概念及分类
    Python:easygui的安装、导入、使用、设置
    Python:模块详解及import本质
    Python:urllib模块的urlretrieve方法
    jQuery操作checkbox实例
    ASP.NET MVC 路由调试工具Router Debugger
    认识Visual Studio 条件编译
  • 原文地址:https://www.cnblogs.com/CCBB/p/1488564.html
Copyright © 2011-2022 走看看