Skip to content

Commit f4fe0e7

Browse files
mdonakaclaude
andcommitted
fix: reduce PalindromicTree memory consumption
Fixes #1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b18849a commit f4fe0e7

2 files changed

Lines changed: 111 additions & 161 deletions

File tree

Library/String/PalindromicTree.hpp

Lines changed: 110 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,203 +1,153 @@
11
#pragma once
2-
#include <iostream>
3-
#include <list>
4-
#include <memory>
2+
#include <algorithm>
53
#include <queue>
64
#include <stack>
75
#include <string>
8-
#include <unordered_map>
96
#include <vector>
107

118
namespace mtd {
12-
auto nullLambda = [](int, const std::list<int>&) {};
139
class PalindromicTree {
14-
// static constexpr auto nullLambda = [](int, const std::list<int>&) {};//
15-
// c++17
16-
17-
class Node : public std::enable_shared_from_this<Node> {
18-
// 回文の右端itr
19-
std::list<int> m_itrs;
20-
// 回文サイズ
21-
const int m_size;
22-
23-
// 最大の回文接尾辞
24-
std::weak_ptr<Node> m_suffixLink;
25-
// 次サイズの回文(囲む文字, 次のNode)
26-
std::unordered_map<char, std::shared_ptr<Node>> m_edges;
27-
28-
// xAxとなるAを探す(x=str[itr])
29-
auto find(int itr, const std::string& s) {
30-
auto p = this->weak_from_this();
31-
while (true) {
32-
auto size = p.lock()->m_size;
33-
// rootにたどり着いた
34-
if (size == -1) { return p; }
35-
// 現在地"A"において"xAx"となる
36-
if (itr - size - 1 >= 0 && s[itr] == s[itr - size - 1]) { return p; }
37-
p = p.lock()->m_suffixLink;
10+
struct Node {
11+
int size;
12+
int suffix_link;
13+
int first_itr = -1;
14+
std::vector<std::pair<char, int>> edges;
15+
std::vector<int> rest_itrs;
16+
17+
Node(int s, int sl) : size(s), suffix_link(sl) {}
18+
19+
auto find_edge(char c) const -> int {
20+
for (const auto& [ch, idx] : edges) {
21+
if (ch == c) { return idx; }
3822
}
23+
return -1;
3924
}
4025

41-
// 新しい回文Nodeを作成する
42-
auto create(int itr, const std::string& s) {
43-
// suffixLinkの探索
44-
auto suffixLinkFrom =
45-
m_suffixLink.lock() /*->m_suffixLink.lock()*/->find(itr, s).lock();
46-
// 新Nodeの作成
47-
auto newNode = std::make_shared<Node>(
48-
m_size + 2, (suffixLinkFrom->m_edges.find(s[itr]) ==
49-
suffixLinkFrom->m_edges.end())
50-
? suffixLinkFrom->m_edges.find(' ')->second
51-
: suffixLinkFrom->m_edges.find(s[itr])->second);
52-
m_edges.emplace(s[itr], newNode);
53-
return std::weak_ptr<Node>(newNode);
54-
}
55-
56-
public:
57-
// constructor
58-
Node(int size, const std::weak_ptr<Node>& suffixLink)
59-
: m_size(size), m_suffixLink(suffixLink) {}
60-
Node() : m_size(-1) {}
61-
62-
// 次サイズの回文を追加
63-
auto add(int itr, const std::string& s) {
64-
auto addRoot = find(itr, s).lock();
65-
auto nextNode =
66-
(addRoot->m_edges.find(s[itr]) == addRoot->m_edges.end())
67-
? addRoot->create(itr, s)
68-
: std::weak_ptr<Node>(addRoot->m_edges.find(s[itr])->second);
69-
nextNode.lock()->m_itrs.emplace_back(itr);
70-
return nextNode;
71-
}
26+
auto add_edge(char c, int idx) -> void { edges.emplace_back(c, idx); }
7227

73-
// debug用
74-
auto outputTree(const std::string& s) const -> void {
75-
if (m_size <= 0) {
76-
std::cerr << "root";
28+
auto add_itr(int itr) -> void {
29+
if (first_itr == -1) {
30+
first_itr = itr;
7731
} else {
78-
//
79-
for (int i = 0; (i < (m_size + 1) / 2); ++i) { std::cerr << " |"; }
80-
std::cerr << "- " << s.substr(*m_itrs.begin() - m_size + 1, m_size);
81-
// 右itr
82-
std::cerr << " [ ";
83-
for (const auto& itr : m_itrs) { std::cerr << itr << " "; }
84-
std::cerr << "] ";
85-
// suffix link
86-
// auto p = m_suffixLink.lock();
87-
// std::cerr << "{" << s.substr(*p->m_itrs.begin() - p->m_size + 1,
88-
// p->m_size) << "} ";
89-
}
90-
std::cerr << "\n";
91-
for (const auto& edge : m_edges) {
92-
if (m_size == -1 && edge.first == ' ') { continue; }
93-
edge.second->outputTree(s);
32+
rest_itrs.push_back(itr);
9433
}
9534
}
9635

97-
// rootを決定
98-
auto isOddRoot(const std::weak_ptr<Node>& evenRoot) {
99-
m_suffixLink = weak_from_this();
100-
m_edges.emplace(' ', evenRoot);
36+
auto get_itrs() const -> std::vector<int> {
37+
if (first_itr == -1) { return {}; }
38+
std::vector<int> result;
39+
result.reserve(1 + rest_itrs.size());
40+
result.push_back(first_itr);
41+
result.insert(result.end(), rest_itrs.begin(), rest_itrs.end());
42+
return result;
10143
}
44+
};
10245

103-
// ラムダ式の実行
104-
template <class Lambda>
105-
auto runLambda(const Lambda& lambda) {
106-
if (m_size > 0) { lambda(m_size, m_itrs); }
46+
const std::string m_s;
47+
std::vector<Node> m_nodes;
48+
static constexpr int ROOT_ODD = 0;
49+
static constexpr int ROOT_EVEN = 1;
50+
51+
auto find(int node_idx, int itr) const -> int {
52+
while (true) {
53+
int size = m_nodes[node_idx].size;
54+
if (size == -1) { return node_idx; }
55+
if (itr - size - 1 >= 0 && m_s[itr] == m_s[itr - size - 1]) {
56+
return node_idx;
57+
}
58+
node_idx = m_nodes[node_idx].suffix_link;
10759
}
60+
}
10861

109-
/*
110-
* lambda: (int size, list<int> rItr) -> void
111-
*/
112-
template <class Lambda, class SuffixLinkLambda = decltype(nullLambda)>
113-
auto dfs_edges(const Lambda& lambda,
114-
const SuffixLinkLambda& slLambda = nullLambda) -> void {
115-
std::stack<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>>>
116-
stk;
117-
stk.emplace(this->weak_from_this().lock());
118-
while (!stk.empty()) {
119-
auto p = stk.top();
120-
stk.pop();
121-
p->runLambda(lambda);
122-
p->m_suffixLink.lock()->runLambda(slLambda);
123-
for (const auto& [_, next_p] : p->m_edges) {
124-
// std::cerr << p->m_size << " -> " << next_p->m_size << std::endl;
125-
stk.emplace(next_p);
126-
}
127-
}
62+
auto add(int node_idx, int itr) -> int {
63+
int add_root = find(node_idx, itr);
64+
char c = m_s[itr];
65+
66+
int existing = m_nodes[add_root].find_edge(c);
67+
if (existing != -1) {
68+
m_nodes[existing].add_itr(itr);
69+
return existing;
12870
}
129-
};
13071

131-
// 対象となる文字列
132-
const std::string m_s;
72+
int new_size = m_nodes[add_root].size + 2;
73+
int suffix_link_from = find(m_nodes[add_root].suffix_link, itr);
74+
75+
int new_suffix_link = m_nodes[suffix_link_from].find_edge(c);
76+
if (new_suffix_link == -1) { new_suffix_link = ROOT_EVEN; }
13377

134-
// 偶数長,奇数長のPalindromicTreeの根(0, -1)
135-
std::shared_ptr<Node> m_rootOdd;
136-
std::shared_ptr<Node> m_rootEven;
78+
int new_idx = static_cast<int>(m_nodes.size());
79+
m_nodes.emplace_back(new_size, new_suffix_link);
80+
m_nodes[new_idx].add_itr(itr);
81+
m_nodes[add_root].add_edge(c, new_idx);
82+
83+
return new_idx;
84+
}
13785

13886
public:
139-
// constructor
140-
PalindromicTree(const std::string& s)
141-
: m_s(s),
142-
m_rootOdd(std::make_shared<Node>()),
143-
m_rootEven(std::make_shared<Node>(0, m_rootOdd)) {
144-
m_rootOdd->isOddRoot(m_rootEven);
145-
auto root = m_rootOdd;
146-
for (int r = 0; r < static_cast<int>(s.size()); ++r) {
147-
root = root->add(r, s).lock();
87+
PalindromicTree(const std::string& s) : m_s(s) {
88+
m_nodes.reserve(s.size() + 2);
89+
m_nodes.emplace_back(-1, ROOT_ODD);
90+
m_nodes.emplace_back(0, ROOT_ODD);
91+
92+
int cur = ROOT_ODD;
93+
for (int i = 0; i < static_cast<int>(s.size()); ++i) { cur = add(cur, i); }
94+
95+
m_nodes.shrink_to_fit();
96+
for (auto& node : m_nodes) {
97+
node.edges.shrink_to_fit();
98+
node.rest_itrs.shrink_to_fit();
14899
}
149100
}
150101

151-
/*
152-
* lambda: (int size, list<int> rItr) -> void
153-
*/
154102
template <class Lambda>
155-
auto dfs_edges(const Lambda& lambda) {
156-
m_rootOdd->dfs_edges(lambda);
103+
auto dfs_edges(const Lambda& lambda) const -> void {
104+
std::stack<int, std::vector<int>> stk;
105+
stk.push(ROOT_ODD);
106+
stk.push(ROOT_EVEN);
107+
108+
while (!stk.empty()) {
109+
int idx = stk.top();
110+
stk.pop();
111+
112+
const auto& node = m_nodes[idx];
113+
if (node.size > 0) { lambda(node.size, node.get_itrs()); }
114+
115+
for (const auto& [_, next_idx] : node.edges) { stk.push(next_idx); }
116+
}
157117
}
158118

159-
/*
160-
* かなり強引な実装
161-
* lambda: (int from, int to) -> void
162-
*/
163119
template <class Lambda>
164-
auto dp_suffixLink(const Lambda& lambda) {
165-
// 森の生成,探索順序の決定
166-
int from;
167-
std::unordered_map<int, int> graph;
168-
std::vector<int> orderCount(m_s.size());
169-
m_rootOdd->dfs_edges(
170-
[&](int, const std::list<int>& rItrs) { from = rItrs.front(); },
171-
[&](int, const std::list<int>& rItrs) {
172-
int to = rItrs.front();
173-
graph.emplace(from, to);
174-
++orderCount[to];
175-
});
176-
// 探索順序に従って処理
177-
std::queue<int, std::list<int>> q;
120+
auto dp_suffixLink(const Lambda& lambda) const -> void {
121+
std::vector<int> order_count(m_s.size(), 0);
122+
std::vector<std::vector<int>> graph(m_s.size());
123+
124+
for (int idx = 2; idx < static_cast<int>(m_nodes.size()); ++idx) {
125+
const auto& node = m_nodes[idx];
126+
if (node.first_itr == -1) { continue; }
127+
128+
int from = node.first_itr;
129+
int sl_idx = node.suffix_link;
130+
if (sl_idx >= 2 && m_nodes[sl_idx].first_itr != -1) {
131+
int to = m_nodes[sl_idx].first_itr;
132+
graph[from].push_back(to);
133+
++order_count[to];
134+
}
135+
}
136+
137+
std::queue<int> q;
178138
for (int i = 0; i < static_cast<int>(m_s.size()); ++i) {
179-
if (orderCount[i] == 0) { q.emplace(i); }
139+
if (order_count[i] == 0) { q.push(i); }
180140
}
141+
181142
while (!q.empty()) {
182143
int f = q.front();
183144
q.pop();
184-
auto range = graph.equal_range(f);
185-
for (auto itr = range.first; itr != range.second; ++itr) {
186-
int t = itr->second;
187-
--orderCount[t];
145+
for (int t : graph[f]) {
146+
--order_count[t];
188147
lambda(f, t);
189-
if (orderCount[t] == 0) { q.emplace(t); }
148+
if (order_count[t] == 0) { q.push(t); }
190149
}
191150
}
192151
}
193-
194-
// debug用
195-
auto outputTree() {
196-
std::cerr << m_s << std::endl;
197-
std::cerr << "-- even --\n";
198-
m_rootEven->outputTree(m_s);
199-
std::cerr << "-- odd --\n";
200-
m_rootOdd->outputTree(m_s);
201-
}
202152
};
203153
} // namespace mtd

Test/String/PalindromicTree_large.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ signed main() {
2525

2626
ll an = a.size();
2727
std::vector<std::pair<ll, ll>> dp(s.size());
28-
tree.dfs_edges([&](int size, const std::list<int>& ritr) {
28+
tree.dfs_edges([&](int size, const std::vector<int>& ritr) {
2929
ll l = 0, r = 0;
3030
for (const auto& x : ritr)
3131
if (x != an && x != an + 1) { ++((x < an) ? l : r); }

0 commit comments

Comments
 (0)