【问题标题】:String Searching Algorithm that uses a graph ? C++使用图的字符串搜索算法? C++
【发布时间】:2019-11-26 21:45:21
【问题描述】:

Code Instructions

大家好。以上是我分配的一个编码项目。我正在阅读说明并且完全迷失了,因为我从未学习过如何编写无向图?不知道我的教授希望我们如何做到这一点,但我希望我能从专家那里得到一些帮助。你们建议我看的任何读物(或提示)以熟悉如何开始该程序?欣赏,谢谢!

【问题讨论】:

  • STL 中没有图形数据结构,所以不使用 STL 的要求对我来说似乎很奇怪。教师对 STL 的这种过分热心有时会过火。
  • 的确,20多年前的我在大学时也是这样,但我还是靠自驱力挺过来了。您不能期望来到 Stack Overflow 之类的网站并要求获得有关如何完成作业的教程。当你在特定的事情上遇到困难时,当你展示你到目前为止所做的事情时,你当然可以提出特定的问题。那么这里的人们将很乐意提供帮助并分享他们的专业知识。尝试查看 Stack Overflow 的帮助部分,从 How do I ask a good question 开始

标签: c++ string search undirected-graph


【解决方案1】:

要解决的问题称为“Word Morph”。你的导师对使用无向图给出了一些限制,其中相邻节点与原点只有一个字符不同。不幸的是,要求不够明确。 “一个字符的不同是模棱两可的。如果我们使用替换插入删除习语,那么我们可以通过比较两个相等大小的字符串来使用其他功能。我假设完整的方法。

而且,最后,您需要找到一条通过图表的最短路径。

我可以向您介绍一种可能的解决方案。一个完整的工作代码示例。

顺便说一下,这个图是无权的,因为从一个节点到下一个节点的成本总是 1。所以实际上我们在谈论一个无向的无权图。

我们这里需要用到的主要算法有:

  • 莱文森。计算 2 个字符串的距离
  • 和广度优先搜索,通过图找到短路路径

请注意,如果单词的长度应该相同,则不需要 Levensthein。只需逐个字符比较字符并计算差异。这很简单。 (但如前所述:说明有点不清楚)

这两种算法都可以修改。例如:您不需要大于 1 的 Levensthein 距离。您可以在找到距离 1 后终止距离计算。而且,在广度优先搜索中,您可以显示您要经过的路径。

好的,现在,如何实现无向图。有两种可能:

  1. 一个矩阵(我不会解释)
  2. 列表或向量

我会推荐这种情况下的矢量方法。矩阵会比较稀疏,所以最好是向量。

您需要的基本数据结构是包含顶点和邻居的节点。所以你有这个词 (a std::string) 作为顶点和“邻居”。这是图中其他节点的索引位置std::vector

图是节点的向量。并且节点邻居指向该向量中的其他节点。我们使用向量中的索引来表示邻居。我们将所有这些打包成一个结构并将其称为“UndirectedGraph”。我们添加了一个“构建”函数来检查邻接关系。在这个函数中,我们将每个字符串与任何字符串进行比较并检查,如果差值小于 2,则为 0 或 1。0 表示相等,1 是给定的约束。如果我们发现这样的差异,我们将其添加为相应节点中的邻居。

此外,我们添加了广度优先搜索算法。描述于Wikipedia

为了简化该算法的实现,我们为节点添加了一个“已访问”标志。

请看下面的代码:

#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <iomanip>
#include <numeric>
#include <algorithm>
#include <queue>

std::istringstream textFileStream(R"#(peach
peace
place
plane
plans
plays
slays
stays
stars
sears
years
yearn
)#");

using Vertex = std::string;
using Edge = std::vector<size_t>;

// One node in a graph
struct Node
{
    // The Vertex is a std::string
    Vertex word{};
    // The edges are the index of the neighbour nodes
    Edge neighbour{};
    // For Breath First Search
    bool visited{ false };

    // Easy input and output
    friend std::istream& operator >> (std::istream& is, Node& n) {
        n.neighbour.clear();
        std::getline(is, n.word);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Node& n) {
        os << std::left << std::setw(25) << n.word << " --> ";
        std::copy(n.neighbour.begin(), n.neighbour.end(), std::ostream_iterator<int>(os, " "));
        return os;
    }
};

// The graph
struct UndirectedGraph
{
    // Contains a vector of nodes
    std::vector<Node> graph;

    // build adjacenies
    void build();

    // Find Path
    bool checkForPathFromStartToEnd(size_t start, size_t end);
    bool checkForPath() {bool result = false;if (graph.size() > 1) {size_t s = graph.size() - 2;size_t e = s + 1;result = checkForPathFromStartToEnd(s, e); }return result; }

    // Easy input and output
    friend std::istream& operator >> (std::istream& is, UndirectedGraph& ug) {
        ug.graph.clear();
        std::copy(std::istream_iterator<Node>(is), std::istream_iterator<Node>(), std::back_inserter(ug.graph));
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const UndirectedGraph& ug) {
        size_t i{ 0 };

        for (const Node& n : ug.graph)
            os << std::right << std::setw(4) << i++ << ' ' << n << '\n';
        return os;
    }

};

// Distance between 2 strings
size_t levensthein(const std::string& string1, const std::string& string2)
{
    const size_t lengthString1(string1.size());
    const size_t lengthString2(string2.size());

    if (lengthString1 == 0) return lengthString2;
    if (lengthString2 == 0) return lengthString1;

    std::vector<size_t> costs(lengthString2 + 1);
    std::iota(costs.begin(), costs.end(), 0);

    for (size_t indexString1 = 0; indexString1 < lengthString1; ++indexString1) {
        costs[0] = indexString1 + 1;
        size_t corner = indexString1;

        for (size_t indexString2 = 0; indexString2 < lengthString2; ++indexString2) {
            size_t upper = costs[indexString2 + 1];
            if (string1[indexString1] == string2[indexString2]) {
                costs[indexString2 + 1] = corner;
            }
            else {
                const size_t temp = std::min(upper, corner);
                costs[indexString2 + 1] = std::min(costs[indexString2], temp) + 1;
            }
            corner = upper;
        }
    }
    size_t result = costs[lengthString2];
    return result;
}

// Build the adjacenies
void UndirectedGraph::build()
{
    // Iterate over all words in the graph
    for (size_t i = 0; i < graph.size(); ++i)
        // COmpare everything with everything (becuase of symmetries, omit half of comparisons)
        for (size_t j = i + 1; j < graph.size(); ++j)
            // Chec distance of the 2 words to compare
            if (levensthein(graph[i].word, graph[j].word) < 2U) {
                // And store the adjacenies
                graph[i].neighbour.push_back(j);
                graph[j].neighbour.push_back(i);
            }
}
bool UndirectedGraph::checkForPathFromStartToEnd(size_t start, size_t end)
{
    // Assume that it will not work
    bool result = false;

    // Store intermediate tries in queue
    std::queue<size_t> check{};

    // Set initial values
    graph[start].visited = true;
    check.push(start);

    // As long as we have not visited all possible nodes
    while (!check.empty()) {
        // Get the next node to check
        size_t currentNode = check.front(); check.pop();
        // If we found the solution . . .
        if (currentNode == end) {
            // The set resultung value and stop searching
            result = true;
            break;
        }
        // Go through all neighbours of the current node
        for (const size_t next : graph[currentNode].neighbour) {
            // If the neighbour node has not yet been visited
            if (!graph[next].visited) {
                // Then visit it
                graph[next].visited = true;
                // And check following elements next time
                check.push(next);
            }
        }
    }
    return result;
}

int main()
{
    // Get the filename from the user
    std::cout << "Enter Filename for file with words:\n";
    std::string filename{};
    //std::cin >> filename;

    // Open the file
    //std::ifstream textFileStream(filename);
    // If the file could be opened . . .
    if (textFileStream) {

        // Create an empty graph
        UndirectedGraph undirectedGraph{};

        // Read the complete file into the graph
        textFileStream >> undirectedGraph;

        Node startWord{}, targetWord{};
        std::cout << "Enter start word and enter target word\n";  // teach --> learn 
        std::cin >> startWord >> targetWord;

        // Add the 2 words at the and of our graph
        undirectedGraph.graph.push_back(startWord);
        undirectedGraph.graph.push_back(targetWord);

        // Build adjacency graph, including the just added words
        undirectedGraph.build();
        // For debug purposes: Show the graph
        std::cout << undirectedGraph;

        std::cout << "\n\nMorph possible?  --> " << std::boolalpha << undirectedGraph.checkForPath() << '\n';
    }
    else {
        // File could not be found or opened
        std::cerr << "Error: Could not open file : " << filename;
    }
    return 0;
}

请注意:虽然我已经实现了询问文件名,但在这个例子中我没有使用它。我从一个字符串流中读取。您需要稍后删除 istringstream 并在现有语句中进行注释。

关于导师的要求:我没有使用任何 STL/Library/Boost 搜索算法。 (为什么?在这个例子中?)但我当然使用其他 C++ STL 容器。我不会重新发明轮子并想出一个新的“向量”或队列。而且我绝对不会使用“新”或 C 样式数组或指针算法。

玩得开心!

对所有其他人:对不起:我忍不住要写代码。 . .

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多