c++ 重新排列单词以匹配首字母和末字母

bbuxkriu  于 2022-12-15  发布在  其他
关注(0)|答案(1)|浏览(163)

如何编写C++代码来重新排列单词顺序,使每个单词的第一个字母(除了第一个)等于前一个单词的最后一个字母?
注意事项:这个问题听起来很具体,几乎没有实际价值。2然而,它很容易解释和理解,并且解决它所需的技术可以应用于需要找到一条完整路径的几个问题,该路径通过一个模拟约束或障碍的图访问每个“节点”正好一次。3这些问题可能令人生畏地难以理解,但是一旦知道了生成树技术和深度优先搜索,就可以直接解决。有关使用相同方法解决几个真实的问题的代码,请参见https://github.com/JamesBremner/obstacles
详情:

  • 输入 * 单词列表,每行一个单词,所有字母均为小写a到z
  • 输出 * 一个有序的单词列表,使每个单词的首字母(第一个除外)等于前一个单词的末字母。列表中的所有单词都必须使用,每个单词只能使用一次。多次提到的单词必须使用相同的次数。
owfi6suc

owfi6suc1#

算法:

  • 通过连接首字母和尾字母匹配的单词构建一个有向图(例如abc -〉cde)
  • 在图中查找生成树。(如果找不到生成树,则单词不能按指定排列)
  • 在字上循环N
  • 从N开始对生成树执行深度优先搜索
  • IF搜索达到每个单词
  • 按到达的顺序输出单词。

该算法通常是有用的,并且可以应用于围绕障碍物(例如https://github.com/JamesBremner/obstacles)的路线规划中的问题
下面是实现该算法的代码:

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>

class cGraph;

/// @brief Spanning tree
class cTree
{
    cGraph &myGraph;

    /// @brief  links in spanning tree
    std::vector<std::pair<int, int>> myLink;

    std::vector<bool> myVisited;

    /// @brief links arranged so last matched next first
    std::vector<std::pair<int, int>> myArrange;

public:
    cTree(cGraph &g)
        : myGraph(g)
    {
    }

    void add(int n1, int n2)
    {
        myLink.push_back(std::make_pair(n1, n2));
    }
    int nodeCount();

    std::string text();

    /// @brief Depth first search starting at a link
    /// @param link
    /// @return true if every word was reached
    bool dfs(
        const std::pair<int, int> &link);

    /// @brief arrange words into required order
    ///
    /// The last letter of each word must be the
    /// same as the first letter of the next word
    std::vector<std::pair<int, int>> arrange();
};

class cGraph
{
    std::vector<std::string> myWord;
    std::vector<std::vector<bool>> myAdj;
    std::vector<std::pair<int, int>> myArrange;

public:
    void setSize(int s)
    {
        myAdj.resize(s, std::vector<bool>(s, false));
    }
    void addLink(int n1, int n2)
    {
        myAdj[n1][n2] = true;
    }

    void read(const std::string &fname);

    void constructAdjacencyMatrix();

    int wordCount() const
    {
        return myWord.size();
    }
    std::string word(int idx) const
    {
        return myWord[idx];
    }

    std::vector<int> sources(int n);
    std::vector<int> targets(int n);

    void arrange();

    void displayArrange();

private:
    cTree spanningTree();
};

int cTree::nodeCount()
{
    std::set<int> nodes;
    for (auto &l : myLink)
    {
        nodes.insert(l.first);
        nodes.insert(l.second);
    }
    return nodes.size();
}

std::string cTree::text()
{
    std::stringstream ss;
    for (auto &l : myLink)
        ss << myGraph.word(l.first)
           << " -> "
           << myGraph.word(l.second)
           << " ";
    return ss.str();
}

std::vector<int> cGraph::sources(int n)
{
    std::vector<int> ret;
    int n1idx = -1;
    for (auto &vl : myAdj)
    {
        n1idx++;
        int n2idx = -1;
        for (bool l : vl)
        {
            n2idx++;
            if (n2idx == n)
                if (l)
                    ret.push_back(n1idx);
        }
    }
    return ret;
}
std::vector<int> cGraph::targets(int n)
{
    std::vector<int> ret;
    int n2idx = -1;
    for (bool l : myAdj[n])
    {
        n2idx++;
        if (l)
            ret.push_back(n2idx);
    }

    return ret;
}

cTree cGraph::spanningTree()
{
    cTree mySpanningTree(*this);
    std::vector<bool> visited(myAdj.size(), false);

    // add initial arbitrary link
    int v, w;
    for (v = 0; v < myAdj.size(); v++)
    {
        auto vt = targets(v);
        if (vt.size())
        {
            w = vt[0];
            mySpanningTree.add(v, w);
            break;
        }
    }
    if (v == myAdj.size())
        throw std::runtime_error("no links");

    visited[v] = true;
    visited[w] = true;

    // while nodes remain outside of span
    bool fdone = false;
    while (!fdone)
    {
        // loop over nodes in span
        for (v = 0; v < myAdj.size(); v++)
        {
            if (!visited[v])
                continue;

            // loop over nodes not in span
            fdone = true;
            for (w = 0; w < myAdj.size(); w++)
            {
                if (visited[w])
                    continue;

                fdone = false;

                if (myAdj[v][w])
                {
                    mySpanningTree.add(v, w);
                    visited[w] = true;
                }
                if (myAdj[w][v])
                {
                    mySpanningTree.add(w, v);
                    visited[w] = true;
                }
            }
        }
        if (v == myAdj.size())
            fdone = true;
    }

    if (mySpanningTree.nodeCount() < myAdj.size())
        throw std::runtime_error(
            "Spanning tree incomplete: no solution available\n");

    // std::cout << "\n"
    //           << mySpanningTree.text() << "\n";

    return mySpanningTree;
}

void cGraph::arrange()
{
    // Find a spanning tree and search it for a matching word arrangement
    // throw exception if no arrangement possible
    myArrange = spanningTree().arrange();
}

void cGraph::displayArrange()
{
    // display the arrangement found
    std::cout << "arrange into "
              << word(myArrange[0].first) << " ";
    for (auto &l : myArrange)
        std::cout << word(l.second) << " ";
    std::cout << "\n";
}

void cGraph::read(const std::string &fname)
{
    std::ifstream ifs(fname);
    if (!ifs.is_open())
    {
        std::cout << "cannot open input\n";
        exit(1);
    }
    std::string word;
    ifs >> word; // skip count
    while (ifs.good())
    {
        ifs >> word;
        myWord.push_back(word);
        std::cout << word << "\n";
    }
    setSize(myWord.size());
}

void cGraph::constructAdjacencyMatrix()
{
    int widx = -1;
    for (auto &w : myWord)
    {
        widx++;
        char first = w[0];
        char last = w[w.length() - 1];
        int sidx = -1;
        for (auto &src : myWord)
        {
            sidx++;
            if (sidx == widx)
                continue;
            if (src[src.length() - 1] == first)
            {
                addLink(sidx, widx);
            }
            if (src[0] == last)
            {
                addLink(widx, sidx);
            }
        }
    }
}

bool cTree::dfs(
    const std::pair<int, int> &l)
{
    myVisited[l.first] = true;
    myVisited[l.second] = true;
    bool fdone = true;
    for (bool v : myVisited)
    {
        if (!v)
        {
            fdone = false;
            break;
        }
    }
    if (fdone)
    {
        myArrange.push_back(l);
        return true;
    }

    auto visited_save = myVisited;
    for (const auto &lnext : myLink)
    {
        if (lnext.first == l.second)
        {
            myVisited = visited_save;
            if (dfs(lnext))
            {
                myArrange.push_back(l);
                return true;
            }
        }
    }
    return false;
}

std::vector<std::pair<int, int>> cTree::arrange()
{
    // loop over links in spanning tree
    for (const auto &l : myLink)
    {
        // do a depth fist search through the tree
        // starting from this link
        myVisited.clear();
        myVisited.resize(myGraph.wordCount(), false);
        if (dfs(l))
        {
            // the dfs reached every word
            // reverse the order in which the words were found
            // which gives an arrangement of words in required order
            std::reverse(myArrange.begin(), myArrange.end());

            // done
            return myArrange;
        }
    }
}

main()
{
    cGraph graph;

    // read word list from file
    graph.read("input.txt");

    // connect matching words
    graph.constructAdjacencyMatrix();

    // arrange words, matching first and last letters
    graph.arrange();

    // output results
    graph.displayArrange();

    return 0;
}

相关问题