检查是否可以链接字符串列表

时间:2012-01-28 11:18:39

标签: c++ string algorithm list

问题

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

ship->petal->lion->nick  = true;
ship->petal   axe->elf   = false;

我的解决方案:

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

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

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

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

修改: 好吧,我的解决方案有问题。我们能否为此得出最佳算法?

8 个答案:

答案 0 :(得分:10)

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

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

  1. 必须连接图表。
  2. 具有最多两个例外的所有顶点具有相同数量的传入和传出边缘。如果存在特殊顶点,则恰好有两个顶点,其中一个顶点比传入边缘多一个外边缘,另一个顶点比传出边缘多一个边缘。
  3. 很容易看出必要性:如果图形具有欧拉路径,则任何此类路径都会遇到除孤立顶点之外的所有顶点(既不是传出边也不是传入边)。通过构造,这里考虑的图中没有孤立的顶点。在欧拉路径中,每次访问顶点时,除了开始和结束之外,都使用一个入射边缘和一个出射边缘,因此每个顶点可能除了起始和结束顶点之外具有相同的入射和出射边缘。除非Eulerian路径是一个循环,否则起始顶点比传入顶点有一个更多的出局边缘,而结尾顶点多一个出局边缘,在这种情况下,所有顶点都有相同的传入和传出边缘。

    现在重要的是这些条件也足够了。人们可以通过感应来证明边缘的数量。

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

    • 记录从单词
    • 获得的所有边和顶点
    • 使用联合查找结构/算法来计算图表的连接组件
    • 记录所有顶点的indegree - outdegree

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

答案 1 :(得分:5)

与臭名昭着的traveling salesman problem不相似吗?

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

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

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

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

答案 2 :(得分:4)

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

ship
pass
lion
nail

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

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

答案 3 :(得分:3)

正如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;
}

但是,使用此谜题的规则无法使用字符串构建此图表:由于 alpha gamma 都连接到 beta ,因此必须以相同的字符结尾(假设它们以'x'结尾),但 gamma 也连接到 delta ,因此 delta 也必须启动用'x'。但是 delta 不能以'x'开头,因为如果确实如此,那么会有一条边alpha -> delta,它不在原始图中。

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

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

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

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:0)

单独检查“Is chainable”并且是“cylcic”

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

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

注意:如果只检查“cylic”链中的第一个和最后一个元素就属于这种情况。

答案 6 :(得分:0)

这可以通过考虑带有N(G)=Σ和E(G)=​​ a->e的有向词aWe的有向图G来减少欧拉路径问题来解决。

答案 7 :(得分:0)

这是一个迭代执行此操作的简单程序:

#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;
}