在 C++ 中用递归替换循环的 N 级
Replacing N-level for loops with recursion in C++
一段时间以来,我一直在尝试想出一种方法来计算单词字符串的所有各种组合。但是,与网络上的大多数组合方法不同,该算法必须生成每个组合,包括所有组合元素不在单个组合中的组合。即,如果我将"你好"、"新"和"世界"组合在一起,我正在寻找的组合是:
HelloNewWorld
HelloNew
HelloWorld
Hello
NewWorld
New
World
我学院的一位教授确实提出了一个快速而肮脏的解决方案来做到这一点,但它使用的是嵌套的 for 循环。
#include <iostream>
#include <vector>
#include <array>
#include <string>
int main()
{
std::vector<std::array<std::string, 2>> vec(3);
vec[0] = {"Hello", ""};
vec[1] = {"New", ""};
vec[2] = {"World", ""};
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
std::cout << vec[0][i] + vec[1][j] + vec[2][k] << std::endl;
}
正如你可能想象的那样,我想要一种方法来使它实际上有点可用和便携。我知道这可以通过递归实现,只是我不知道如何实现它。理想情况下,如果可能的话,我想使这个尾递归,因为计划是计算非常大的组合。递归这样做的最佳方法是什么,使尾递归容易吗?
在每个级别上,当它到达所有单词的末尾时,它会递归,无论是否使用当前单词打印结果:
#include <iostream>
#include <string>
#include <vector>
void recurse(std::vector<std::string> &values,size_t level,std::string str) {
if (level<values.size()) {
recurse(values,level+1,str+values[level]);
recurse(values,level+1,str);
} else {
std::cout<<str<<"n";
}
}
int main(int argc, char*argv[]) {
if (argc<2)
std::cout<<argv[0]<<" <word> [<word> [...]]n";
else {
std::vector<std::string> values;
for(int i=1;i<argc;++i) {
values.push_back(argv[i]);
}
recurse(values,0,"");
}
return 0;
}
当与./a.out Hello New World
一起运行时,会产生:
HelloNewWorld
HelloNew
HelloWorld
Hello
NewWorld
New
World
对于 N 个元素的向量,您可以使用从k=1
到k=N
的所有组合来非常有效地做到这一点。使用此处提供的Howard Hinnant的库,您可以相当有效地使用它。就我而言,我将库命名为sampling.h
,这是唯一的外部依赖项,可以在此处完整查看。
#include "sampling.h"
#include <iostream>
#include <vector>
/**
* This function can take any container that has a bidirectional
* iterator (std::list, std::deque, std::vector) that contains elements
* of type std::string or similar, that must implement an `operator+`
* and `operator<<` for printing.
*/
template <typename BiDirStringContainer>
void print_combinations(BiDirStringContainer& container)
{
auto first = container.begin();
auto last = container.end();
for (size_t i = 1; i <= container.size(); ++i) {
auto mid = first + i;
for_each_combination(first, mid, last, [](auto f, auto l) {
std::string w;
for (; f != l; ++f) {
w += *f;
}
std::cout << w << std::endl;
return false;
});
}
}
int main(void)
{
std::vector<std::string> words = {
"Hello",
"New",
"World",
};
print_combinations(words);
return 0;
}
使用 C++14 标准编译并运行它输出:
Hello
New
World
HelloNew
HelloWorld
NewWorld
HelloNewWorld
这正是您的帖子所描述的。由于 lambda 是一个自定义函子,并且可以存储状态,因此您可以对这些组合做任何您想做的事情:存储副本、打印它们等。
这比您在标准库中无需大量工作或从标准库的建议中获得的任何内容都要快得多。例如,std::next_combination
和std::next_permutation
(前者不包括在内,但在这里建议(。我强烈建议阅读霍华德·欣南特(Howard Hinnant(的博客文章:它很有启发性。他的实现的时间复杂度和蛮力速度超过了大多数其他建议。如果您需要高性能组合或排列,他已经为您完成了工作。
如果我理解正确,您希望生成字符串的所有组合。 在这种情况下,您可以使用 BFS 以及集合和队列来生成组合,我将尝试解释。
假设您的字符串ABCD
.您有一个要向其添加ABCD
的队列和一个要立即向其添加ABCD
的队列
while the queue is not empty
1) you pop the top element
2) you generate substrings of that popped element
a) if that substring is not in the set add it to the queue
要在步骤 2 中生成子字符串,请执行以下操作
for(int i =0;i<string.length();i++)
{
string substr1 = string.substr(0,i);
string substr2 = string.substr(i,string.length()-1);
string substring = substr1+substr2;
}
在ABCD
(输入字符串(上执行此操作将生成BCD
、ACD
和ABD
和ABC
。 现在将这 3 个添加到集合和队列中
现在,您将BCD
、ACD
和ABD
添加到集合中。假设BCD
是queue.front()
.你弹出它并生成CD
、BD
和BC
,并将它们添加到set
和队列中。当您弹出ACD
next 时,您会生成CD
、AD
和AC
但现在您不会将CD
添加到队列中,因为它在集合中。
编辑:
我看到了你的问题,我的答案适用于字符串,但你可以在vector<string>
上使用相同的原理来生成所有组合ABCD
这只会Hello(A)World(B)...
如果唯一的可能性是单词出现或不出现,则有两种可能性。所以对于 n 个单词,你有 2^n 个组合。因此,您只需从 0(包括(到 2^n-1(包括(数 2^n 个数字,并将每个位映射到一个单词。
不需要递归,只需一个循环计数。
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- 将"-01"替换为"-02" 英特尔编译器选项会导致 FPE 在较小的 for 循环行程计数中抛出
- 使用基于数组和范围的 For 循环替换一些基本代码行
- 什么可以用来替换代码中的循环和 if 语句?
- C++代码在 for 循环的条件下给出运行时错误,而如果它被具有相同意义的代码替换,则编译正确
- C++ 转换和 lambda - 替换循环
- 在 C++ 中用递归替换循环的 N 级
- 将 for 循环替换为 while 循环
- 使用标准库将循环替换为 strtok
- 将循环(以及其中的条件)替换为按索引直接访问
- 尝试在自定义 QT 创建器文本编辑器中创建查找和替换循环.我似乎无法设置光标位置
- 如何在循环中替换旧值C++
- 用memcopy、memmove或std:copy替换For循环
- 通过宏替换展开循环无效
- 如何使用std算法来替换for循环
- 用指针替换循环索引器有什么好处吗?
- 如何替换我的"for"循环以通过 STL mimax 算法找到最小值/最大值
- 用 while 循环替换我的 for 循环时我做错了什么
- C++我可以用1个循环替换嵌套的For循环吗
- 使用循环替换字符串中的字符(需要帮助优化)