返回信息流leetcode 472 题目:
同样的算法(trie树+dfs),java能过,但是c++就莫名奇妙的超出内存限制......
考虑了是否是自己实现的问题,修修改改了几个小时还是一样内存超限
代码如下:
struct trie_node {
shared_ptr<trie_node> children[26] = { nullptr };
bool isword = false;
};
void trie_extend(shared_ptr<trie_node> root, string &s) {
for (auto c : s) {
int childindex = c - 'a';
if (!root->children[childindex])
root->children[childindex] = make_shared<trie_node>();
root = root->children[childindex];
}
root->isword = true;
}
bool search(shared_ptr<trie_node> root, string &s) {
stack<pair<shared_ptr<trie_node>, int>> status;
status.push({ root, 0 });
bool res = false;
while (!status.empty()) {
auto p = status.top(); status.pop();
int index = p.second;
shared_ptr<trie_node> now = p.first;
if (index == s.size()) {
if ( now->isword) res = true;
continue;
}
int childindex = s[index] - 'a';
if (now->isword)
status.push({ root, index });
if (now->children[childindex])
status.push({ now->children[childindex], index+1 });
}
return res;
}
class Solution {
public:
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
sort(words.begin(), words.end(), [](const string &a, const string &b) {
return a.size() < b.size();
});
shared_ptr<trie_node> root = make_shared<trie_node>();
vector<string> res;
for (auto &word : words) {
if (word.empty()) continue;
if (search(root, word)) res.push_back(word);
else trie_extend(root, word);
}
return res;
}
};
所做过的修改:
1.将search改为非递归,防止字符串过长导致栈溢出
2.使用shared_ptr代替指针,防止oj测试使用在计算内存使用量的时候把所有用例的加在一起
3.对于search成功的串,不再添加在trie树中
很气的是,显示的失败用例是我觉得用内存很少的一个用例......
有时间的朋友可以帮帮忙找找原因,不胜感激!
这是一条镜像帖。来源:北邮人论坛 / cpp / #95655同步于 2017/6/23
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
leetcode472 C++代码蜜汁内存超限......
intmain
2017/6/23镜像同步7 回复
订阅后,新回复会通过你的通知中心匿名送达。
7 条回复
看了半天没找到问题。怀疑是别的case爆了然后在这才出现问题,而且试了提交一下,发现结果是44/44 case pass,然后再给你一个result MLE。我用golang写的过程和你差不多,占用的内存可能还比你的大。因为你的这个case,看了下只有一个status的size和trie_node的深度只有1。不过频繁的push pop,感觉更应该是TLE. 在其他case中,倒是有可能因为status太大导致问题。
话说楼主你的new/delete版本是什么样的,我在想会不会是gc不及时导致的问题。
想了半天,用个黑魔法可以解决问题。就是直接把内存分配在静态存储区上。
下面这个版本可以过所有case。
```
struct trie_node {
trie_node* children[26];
bool isword = false;
};
int cnt = 0;
static trie_node tmp[100000];
int idx = 0;
void trie_extend(trie_node* root, string &s) {
for (auto c : s) {
int childindex = c - 'a';
if (!root->children[childindex]){
root->children[childindex] = &tmp[idx];
idx++;
}
root = root->children[childindex];
}
root->isword = true;
}
bool search(trie_node* root, string &s) {
stack<pair<trie_node*, int> > status;
status.push({ root, 0 });
bool res = false;
while (!status.empty()) {
auto p = status.top(); status.pop();
int index = p.second;
trie_node* now = p.first;
if (index == s.size()) {
if (now->isword) res = true;
continue;
}
int childindex = s[index] - 'a';
if (now->isword)
status.push({ root, index });
if (now->children[childindex])
status.push({ now->children[childindex], index + 1 });
}
return res;
}
class Solution {
public:
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
sort(words.begin(), words.end(), [](const string &a, const string &b) {
return a.size() < b.size();
});
idx = 0;
memset(&tmp, 0, sizeof(tmp));
trie_node* root = &tmp[idx];
idx++;
vector<string> res;
for (auto &word : words) {
if (word.empty()) continue;
if (search(root, word)) res.push_back(word);
else trie_extend(root, word);
}
return res;
}
};
```
【 在 NachtZ 的大作中提到: 】
: [md]
: 看了半天没找到问题。怀疑是别的case爆了然后在这才出现问题,而且试了提交一下,发现结果是44/44 case pass,然后再给你一个result MLE。我用golang写的过程和你差不多,占用的内存可能还比你的大。因为你的这个case,看了下只有一个status的size和trie_node的深度只有1。不过频繁的push pop,感觉更应该是TLE. 在其他case中,倒是有可能因为status太大导致问题。
: 话说楼主你的new/delete版本是什么样的,我在想会不会是gc不及时导致的问题。
: ...................
首先谢谢你耐心的帮我想了这个问题!
gc的话,最初我也是用一般指针做的,然后只new而没有delete(怕麻烦......)
然后考虑到是不是这个原因引起的,所以改用了c++11 的shared_ptr, 它是基于引用计数的,计数为0就自动回收内存了。但是还是MLE了
然后关于栈的push, pop的话,原本的版本search用的是递归,最后一个例子字符串最长为1000,担心栈溢出,所以用栈实现的非递归。然而没什么卵用。
感觉leetcode的判题机制对c++不太友好...
顺带吐槽一下,这个是什么鬼......
我是觉得leetcode是有可能出错的。因为leetcode对golang也很不友好,特别是内存占用和递归方面。
你的答案,用递归。在有些case中,很有可能会暴栈。不过这个case应该不会。这一题的case貌似没那么恶心,要不然dfs很容易就TLE了。
然后你的res为true之后应该尽快返回,而不应该continue。
至于shared_ptr,我不是在说你没有回收的事情,我怀疑的是shared_ptr不一定是及时回收,而不是说不回收。比如golang中,gc是隔一段时间触发一次的,递归着色的检索分配的内存。如果c++中,也是隔一段时间触发一次。而不是说在计数器为0的时候直接触发,是有可能报个MLE的。当然,如果因为这个原因的话,其实golang也很有可能暴的。
不过我试了下,手动new和手动delete,还是MLE.我现在觉得是leetcode的问题。
【 在 intmain 的大作中提到: 】
: 首先谢谢你耐心的帮我想了这个问题!
: gc的话,最初我也是用一般指针做的,然后只new而没有delete(怕麻烦......)
: 然后考虑到是不是这个原因引起的,所以改用了c++11 的shared_ptr, 它是基于引用计数的,计数为0就自动回收内存了。但是还是MLE了
: ...................
【 在 NachtZ 的大作中提到: 】
: 我是觉得leetcode是有可能出错的。因为leetcode对golang也很不友好,特别是内存占用和递归方面。
: 你的答案,用递归。在有些case中,很有可能会暴栈。不过这个case应该不会。这一题的case貌似没那么恶心,要不然dfs很容易就TLE了。
: 然后你的res为true之后应该尽快返回,而不应该continue。
: ...................
(⊙v⊙)嗯,那个continue...
本来while判断条件里面是有!res的,修修改改的不知道什么时候把他去掉了,╮(╯▽╰)╭
手动回收都MLE,(⊙o⊙)…感觉leetcode的内存使用判断有问题啊
你发邮件问leetcode管理员吧。这应该是个bug。
然后我在代码中判断输入跳过最后一个case。还是MLE。╮(╯▽╰)╭问题果然不在这。
【 在 intmain 的大作中提到: 】
:
: (⊙v⊙)嗯,那个continue...
: 本来while判断条件里面是有!res的,修修改改的不知道什么时候把他去掉了,╮(╯▽╰)╭
: ...................
加set记录状态,防止重复入栈。
另外要定义在全局上。
```c++
struct trie_node {
shared_ptr<trie_node> children[26] = { nullptr };
bool isword = false;
};
void trie_extend(shared_ptr<trie_node> root, string &s) {
for (auto c : s) {
int childindex = c - 'a';
if (!root->children[childindex])
root->children[childindex] = make_shared<trie_node>();
root = root->children[childindex];
}
root->isword = true;
}
stack<pair<shared_ptr<trie_node>, int>> status;
set<pair<shared_ptr<trie_node>, int> > hh;
bool search(shared_ptr<trie_node> root, string &s) {
while(status.size())
status.pop();
hh.clear();
status.push({ root, 0 });
bool res = false;
while (!status.empty()) {
auto p = status.top(); status.pop();
hh.insert(p);
int index = p.second;
shared_ptr<trie_node> now = p.first;
if (index == s.size()) {
if ( now->isword) res = true;
continue;
}
int childindex = s[index] - 'a';
if (now->isword&&!hh.count({ root, index }))
status.push({ root, index });
if (now->children[childindex]&&!hh.count({ now->children[childindex], index+1 }))
status.push({ now->children[childindex], index+1 });
}
return res;
}
class Solution {
public:
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
sort(words.begin(), words.end(), [](const string &a, const string &b) {
return a.size() < b.size();
});
shared_ptr<trie_node> root = make_shared<trie_node>();
vector<string> res;
for (auto &word : words) {
if (word.empty()) continue;
if (search(root, word)) res.push_back(word);
else trie_extend(root, word);
}
return res;
}
};