问题
实现一个函数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。如果是这样,列表是可链接的。如果列表是循环的,则最终列表为空。
但我认为我在这里遗漏了一些案例,
修改: 好吧,我的解决方案有问题。我们能否为此得出最佳算法?
答案 0 :(得分:10)
问题是检查有向图中是否存在Eulerian path,其顶点是作为至少一个提供的单词的第一个或最后一个字母出现的字母,其边是提供的单词(每个单词是从第一个字母到最后一个字母的边缘。
在这些图中存在欧拉路径的一些必要条件:
很容易看出必要性:如果图形具有欧拉路径,则任何此类路径都会遇到除孤立顶点之外的所有顶点(既不是传出边也不是传入边)。通过构造,这里考虑的图中没有孤立的顶点。在欧拉路径中,每次访问顶点时,除了开始和结束之外,都使用一个入射边缘和一个出射边缘,因此每个顶点可能除了起始和结束顶点之外具有相同的入射和出射边缘。除非Eulerian路径是一个循环,否则起始顶点比传入顶点有一个更多的出局边缘,而结尾顶点多一个出局边缘,在这种情况下,所有顶点都有相同的传入和传出边缘。
现在重要的是这些条件也足够了。人们可以通过感应来证明边缘的数量。
这样可以进行非常有效的检查:
indegree - outdegree
如果number of components > 1
或者(至少)有一个带有|indegree - outdegree| > 1
的顶点或者有两个以上带有indegree != outdegree
的顶点,则这些单词不可链接,否则它们就是。
答案 1 :(得分:5)
与臭名昭着的traveling salesman problem不相似吗?
如果你有n
个字符串,你可以用它们构建一个图形,其中每个节点对应一个字符串。您可以通过以下方式构造边:
a
和b
是可链接的,则引入权重为a -> b
的边1
。a
和b
,您引入了权重为a -> b
的边n
。然后,当且仅当您可以在权重小于2n
的图表中找到最佳TSP路线时,所有字符串都是可链接的(不重复)。
注意:您的问题实际上比TSP更简单,因为您始终可以将字符串链转换为TSP,但不一定相反。
答案 2 :(得分:4)
以下是您的算法不起作用的情况:
ship
pass
lion
nail
您的开始和结束列表都是s, p, l, n
,但您不能创建一个链(您有两个链 - ship->pass
和lion->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)
如果您将petal
和lion
替换为pawn
和label
,您仍然拥有:
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;
}