【问题标题】:Checking if a list of strings can be chained检查是否可以链接字符串列表
【发布时间】:2012-02-21 02:59:23
【问题描述】:

问题

实现一个函数bool chainable(vector<string> v),它将一组字符串作为参数,如果它们可以被链接,则返回true。如果第一个字符串以第二个开头的相同字符结尾,则可以链接两个字符串,例如:

ship->petal->lion->nick  = true;  (original input is list not LL)
ship->petal   axe->elf   = false;

我的解决方案:

我的逻辑是,如果它是可链接的,那么只有一个开始和结束不匹配。所以我创建了一个开始列表和一个结束列表。像这样。

starts:s,p,l,n
ends:  p,l,n,k

如果我删除公共元素,列表应该最多包含一项。即 s 和 k。如果是这样,列表是可链接的。如果列表是循环的,则最终列表为空。

但我认为我在这里遗漏了一些案例,

编辑: 好吧,显然我的解决方案有问题。我们能总结出最好的算法吗?

【问题讨论】:

  • 我很确定每个字符串都可以链接。您对我们隐藏的缺失规则是什么?
  • ?见 {ship,petal,axe,elf} 你怎么能用这些做一条链子?第一个的结束字母应该是第二个的开始以形成一个链。
  • 啊!这是关于字母的结尾和开头必须相同。感谢您的澄清:)
  • 你会如何没有电脑?这很可能是最简单的解决方案。
  • 如果是 ship->petal->lion->link->ship->petal 怎么办。这里的开始和结束并不指向船,但它更像是一个循环循环,在最后一种情况下循环不完整?

标签: c++ string algorithm list


【解决方案1】:

问题是检查有向图中是否存在Eulerian path,其顶点是作为至少一个提供的单词的第一个或最后一个字母出现的字母,并且其边缘是提供的单词(每个单词都是边缘从第一个字母到最后一个字母)。

此类图中欧拉路径存在的一些必要条件:

  1. 必须连接图表。
  2. 最多有两个例外的所有顶点具有相同数量的传入和传出边。如果存在异常顶点,则正好有两个,其中一个的出边比入边多一条,另一个入边比出边多一条。

必然性很容易看出:如果一个图有欧拉路径,那么任何这样的路径都会满足除孤立顶点之外的所有顶点(既不是出边也不是入边)。通过构造,这里考虑的图中没有孤立的顶点。在欧拉路径中,每次访问一个顶点时,除了起点和终点外,使用一条入边和一条出边,因此每个顶点(可能除了开始和结束顶点)具有相同数量的入边和出边。除非欧拉路径是一个环,否则起始顶点的出边比入边多一条,而结束顶点的入边条多一条,除非欧拉路径是一个环,在这种情况下,所有顶点的入边和出边数量均等。

现在重要的是这些条件也足够了。可以通过对边数的归纳来证明这一点。

这样可以进行非常有效的检查:

  • 记录从单词中获得的所有边和顶点
  • 使用联合查找结构/算法来计算图的连通分量
  • 为所有顶点记录indegree - outdegree

如果number of components > 1 或(至少)有一个顶点带有|indegree - outdegree| > 1 或有两个以上带有indegree != outdegree 的顶点,则这些词不可链接,否则它们是。

【讨论】:

  • 我认为,这个问题更像是寻找哈密顿路径而不是欧拉路径,因为我只能使用一次单词,而在欧拉路径中,顶点可以多次使用。跨度>
  • 但在欧拉路径中,每个只使用一次。 words 是我模型中的 edges,顶点是字母(单词的第一个/最后一个字母)。所以在我的模型中它确实是欧拉而不是哈密顿。
  • 哦,是的。道歉。顺便说一句,我能找到找到欧拉路径的解决方案吗?还是让我对算法有基本了解的教程?
  • 对于寻找欧拉路径,Hierholzer's method 非常有效。对于有向图和/或两个奇数异常点的出现,您必须稍微修改它,但基本原则仍然成立。关于草图算法union find is explained here,另一部分应该很容易实现;为了理解,我不知道。有一些拓扑学和同源性知识就很明显了,但是没有?
  • 这是正确的答案,但我个人更喜欢减少欧拉路径,而不是欧拉路径何时存在于图中。也就是说,这个问题的有趣之处在于归约到欧拉路径。
【解决方案2】:

这不是和臭名昭著的traveling salesman problem很像吗?

如果你有n 字符串,你可以用它们构造一个图,其中每个节点对应一个字符串。您可以通过以下方式构建边缘:

  • 如果字符串(对应的节点)ab 是可链接的,则引入权重为 1 的边 a -> b
  • 对于所有不可链接的字符串(分别为节点)ab,您引入了一条边 a -> b,权重为 n

然后,当且仅当您可以在图中找到权重小于2n 的最佳 TSP 路线时,您的所有字符串都是可链接的(不重复)。

注意:您的问题实际上比 TSP 更简单,因为您始终可以将字符串链接转换为 TSP,但不一定反过来。

【讨论】:

    【解决方案3】:

    这是您的算法不起作用的情况:

    ship
    pass
    lion
    nail
    

    你的开始和结束列表都是s, p, l, n,但你不能创建一个链(你会得到两个链-ship->passlion->nail)。

    递归搜索可能是最好的——选择一个起始词 (1),然后,对于可以跟随它的每个词 (2),尝试解决创建以 (2) 开头的链的较小问题包含除 (1) 之外的所有单词。

    【讨论】:

    • 是的,你是对的。谢谢!我知道我错过了一些东西,不可能那么容易。您能想到的解决方法有哪些?
    • 你有一个算法吗?
    【解决方案4】:

    正如 phimuemue 所指出的,这是一个图形问题。您有一组带有(有向)边的字符串(顶点)。显然,图表必须是connected 才能链接——这很容易检查。不幸的是,除此之外的规则有点不清楚:

    如果字符串可以多次使用,但链接不能,那么问题是找到一个Eulerian path,这可以有效地完成。欧拉路径使用每条边一次,但可能多次使用顶点。

    // this can form a valid Eulerian path
    yard
    dog
    god
    glitter
    
    yard -> dog -> god -> dog -> glitter
    

    如果字符串不能被多次使用,那么问题就在于找到Hamiltonian path。由于哈密顿路径问题是 NP 完全的,因此不知道确切的有效解决方案。当然,对于小的 n,效率并不是很重要,蛮力解决方案可以正常工作。

    然而,事情并不是那么简单,因为可以作为这个问题的输入出现的图表集是有限的。例如,以下是一个有效的有向图(dot 表示法)(*)。

    digraph G {
        alpha -> beta;
        beta -> gamma;
        gamma -> beta;
        gamma -> delta;
    }
    

    然而,这个图不能使用这个谜题的规则从字符串构造:因为 alphagamma 都连接到 beta,它们必须以相同的字符结尾(假设它们以 'x' 结尾),但 gamma 也连接到 delta,因此 delta 也必须开始与'x'。但是delta不能以'x'开头,因为如果是这样,那么就会有一条边alpha -> delta,它不在原始图中。

    因此,这与哈密顿路径问题完全相同,因为输入集受到更多限制。即使不存在解决哈密顿路径问题的有效算法,也可能存在解决字符串链接问题的有效算法。

    但是...我不知道那个算法会是什么。也许其他人会想出一个真正的解决方案,但同时我希望有人觉得这个答案很有趣。

    (*) 它也恰好有一条哈密顿路径:alpha -> beta -> gamma -> delta,但这与后面的内容无关。

    【讨论】:

    • 考虑到我对图表的了解是新的和稀疏的,我不明白你说的大部分内容。 :)。我现在正在研究图表。很快就会回复。但我确实明白这听起来确实像一个图形问题。
    【解决方案5】:

    如果将petallion 替换为pawnlabel,您仍然有:
    starts:s,p,l,n
    ends: p,l,n,k

    你的算法决定了它的可链接性,但它们不是。
    问题是你断开了每个单词的第一个和最后一个字母。

    递归回溯或动态规划算法应该可以解决这个问题。

    【讨论】:

      【解决方案6】:

      分别检查“可链接”和“循环”

      如果它是循环的,它必须首先是可链接的。你可以这样做:

      if (IsChainable)
      {
        if (IsCyclic() { ... }
      }
      

      注意:如果您只检查链的第一个和最后一个元素是否为“cylic”,就会出现这种情况。

      【讨论】:

        【解决方案7】:

        这可以通过减少欧拉路径问题来解决,方法是考虑一个有向图 G,其中 N(G) = Σ 和 E(G) = a->e 表示单词 aWe

        【讨论】:

          【解决方案8】:

          下面是一个简单的迭代程序:

          #include <string>
          #include <vector>
          #include <iostream>
          
          using std::vector;
          using std::string;
          
          bool isChained(vector<string> const& strngs)
          {
              if (strngs.size() < 2) return false;      //- make sure we have at least two strings
              if (strngs.front().empty()) return false; //- make sure 1st string is not empty
          
              for (vector<string>::size_type i = 1; i < strngs.size(); ++i)
              {
                  string const& head = strngs.at(i-1);
                  string const& tail = strngs.at(i);
                  if (tail.empty())  return false;
                  if (head[head.size()-1] != tail[0])  return false;
              }
          
              return true;
          }
          
          int main()
          {
              vector<string>  chained;
              chained.push_back("ship");
              chained.push_back("petal");
              chained.push_back("lion");
              chained.push_back("nick");
          
              vector<string>  notChained;
              notChained.push_back("ship");
              notChained.push_back("petal");
              notChained.push_back("axe");
              notChained.push_back("elf");
          
              std::cout << (isChained(chained) ? "true" : "false") << "\n";     //- prints 'true'
              std::cout << (isChained(notChained) ? "true" : "false") << "\n";  //- prints 'false'
          
              return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-12-18
            • 1970-01-01
            • 2013-01-09
            • 1970-01-01
            • 2015-02-17
            • 2013-04-29
            • 1970-01-01
            相关资源
            最近更新 更多