【发布时间】:2017-08-18 07:40:48
【问题描述】:
我比 C++ 更熟悉 C#,所以我必须就这个问题寻求建议。我不得不将一些代码片段重写为 C++,然后(令人惊讶地)遇到了性能问题。
我已将问题缩小到这些 sn-ps:
C#
public class SuffixTree
{
public class Node
{
public int Index = -1;
public Dictionary<char, Node> Children = new Dictionary<char, Node>();
}
public Node Root = new Node();
public String Text;
public SuffixTree(string s)
{
Text = s;
for (var i = s.Length - 1; i >= 0; --i)
InsertSuffix(s, i);
}
public void InsertSuffix(string s, int from)
{
var cur = Root;
for (int i = from; i < s.Length; ++i)
{
var c = s[i];
if (!cur.Children.ContainsKey(c))
{
var n = new Node() { Index = from };
cur.Children.Add(c, n);
return;
}
cur = cur.Children[c];
}
}
public bool Contains(string s)
{
return FindNode(s) != null;
}
private Node FindNode(string s)
{
var cur = Root;
for (int i = 0; i < s.Length; ++i)
{
var c = s[i];
if (!cur.Children.ContainsKey(c))
{
for (var j = i; j < s.Length; ++j)
if (Text[cur.Index + j] != s[j])
return null;
return cur;
}
cur = cur.Children[c];
}
return cur;
}
}
}
C++
struct node
{
int index;
std::unordered_map<char, node*> children;
node() { this->index = -1; }
node(int idx) { this->index = idx; }
};
struct suffixTree
{
node* root;
char* text;
suffixTree(char* str)
{
int len = strlen(str) + 1;
this->text = new char[len];
strncpy(this->text, str, len);
root = new node();
for (int i = len - 2; i >= 0; --i)
insertSuffix(str, i);
}
void insertSuffix(char* str, int from)
{
node* current = root;
for (int i = from; i < strlen(str); ++i)
{
char key = str[i];
if (current->children.find(key) == current->children.end())
{
current->children[key] = new node(from);
return;
}
current = current->children[key];
}
}
bool contains(char* str)
{
node* current = this->root;
for (int i = 0; i < strlen(str); ++i)
{
char key = str[i];
if (current->children.find(key) == current->children.end())
{
for (int j = i; j < strlen(str); ++j)
if (this->text[current->index + j] != str[j])
return false;
return true;
}
current = current->children[key];
}
}
}
在这两种情况下,我都会创建一个后缀树,然后在一个更大的函数中使用它,该函数与帖子无关(我们称之为 F())。我已经在两个随机生成的长度为 100000 的字符串上进行了测试。C# 版本构建了我的后缀树并在 F() 中使用它,总执行时间为:480 ms 而我的代码ve “翻译成 C++” 在 48 秒
内执行我对此进行了进一步研究,似乎在我的 C++ 代码中,构造函数需要 47 秒,而在 F() 中使用树的运行时间为 48 毫秒这比 C# 快 10 倍。
结论
看来主要问题出在insertSuffix()上,可能是我对unordered_map结构缺乏了解和理解。任何人都可以对此有所了解吗?我是否在 C++ 变体中犯了一些菜鸟错误,导致对象构造需要这么长时间?
附加信息
我已经编译了 C# 和 C++ 程序以获得最大速度 /O2(发布)
【问题讨论】:
-
for (int i = from; i < strlen(str); ++i)这太贵了。在循环的每次迭代中,您都会重新计算char*字符串的长度。如果您使用 C++,请使用std::string -
如果我之前将长度存储在变量中并继续使用char*可以吗?使用 std::string 还有其他好处吗?我会在稍后测试您的建议。
-
使用
char*是一个非常糟糕的主意。使用std::string。 没有理由使用char*而不是std::string。 -
贾斯汀 我可以确认您已经提供了解决方案。我想我已经看了太久了,并没有发现这个明显的错误嘿嘿。请善意将其发布为答案,以便我接受并投票。非常感谢!
标签: c# c++ algorithm optimization suffix-tree