From 4c12f8c3a3ea66c8ab9988a5b6f650f6d313d1d7 Mon Sep 17 00:00:00 2001 From: Ahmed Dardery Date: Sat, 23 Oct 2021 13:56:46 +0200 Subject: [PATCH 1/2] new: Palindromic Tree --- content/strings/PalindromicTree.h | 58 ++++++++++++++++++++++++ content/strings/chapter.tex | 1 + stress-tests/strings/PalindromicTree.cpp | 32 +++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 content/strings/PalindromicTree.h create mode 100644 stress-tests/strings/PalindromicTree.cpp diff --git a/content/strings/PalindromicTree.h b/content/strings/PalindromicTree.h new file mode 100644 index 000000000..34497244b --- /dev/null +++ b/content/strings/PalindromicTree.h @@ -0,0 +1,58 @@ +/** + * Author: Ahmed Dardery + * Date: 2020-12-24 + * License: CC0 + * Source: https://codeforces.com/blog/entry/13959 + * Description: Builds a palindromic tree over a string + * 0 is the imaginary string, 1 is the empty string + * [2, n) are the palindromes, s.substr(pos[i], len[i]), occurs freq[i] + * fail[i] is the longest suffix palindrome of ith palindrome + * Time: O(n) + * Status: stress-tested + */ +#pragma once + +struct PalinTree { + const int A = 128; + string str; + vi fail, len, pos, lz, freq; + vector nxt; + int n, cur; + + PalinTree(const string &s) : str(s) { + fail = len = pos = lz = freq = vi(sz(s) + 2); + nxt.resize(sz(s) + 2); + n = cur = fail[0] = fail[1] = 0; + addNode(-1, -1), addNode(0, 0); + for(int i = 0; i < sz(s); ++i) addChar(i, s[i]); + propagate(); + } + + void addChar(int i, int c) { + int u = getFailure(cur, i); + int &ch = nxt[u][c]; + if (~ch) return (void) ++lz[cur = ch]; + int v = cur = ch = addNode(len[u] + 2, i - len[u] - 1); + fail[v] = len[v] == 1 ? 1 : nxt[getFailure(fail[u], i)][c]; + } + + int addNode(int l, int p) { + nxt[n].assign(A, -1); + len[n] = l, pos[n] = p, lz[n] = 1, freq[n] = 0; + return n++; + } + + void propagate() { + for (int i = n - 1; ~i; --i) { + freq[i] += lz[i]; + lz[fail[i]] += lz[i]; + lz[i] = 0; + } + } + + int getFailure(int u, int i) { + while (i <= len[u] || str[i] != str[i - len[u] - 1]) u = fail[u]; + return u; + } +}; + \ No newline at end of file diff --git a/content/strings/chapter.tex b/content/strings/chapter.tex index 7db9b774e..7fa02e708 100644 --- a/content/strings/chapter.tex +++ b/content/strings/chapter.tex @@ -8,3 +8,4 @@ \chapter{Strings} \kactlimport{SuffixTree.h} \kactlimport{Hashing.h} \kactlimport{AhoCorasick.h} +\kactlimport{PalindromicTree.h} diff --git a/stress-tests/strings/PalindromicTree.cpp b/stress-tests/strings/PalindromicTree.cpp new file mode 100644 index 000000000..e61a351ba --- /dev/null +++ b/stress-tests/strings/PalindromicTree.cpp @@ -0,0 +1,32 @@ +#include "../utilities/template.h" +#include "../../content/strings/PalindromicTree.h" + + +int main() { + rep(alpha, 2, 27) { + rep(t, 0, 10000) { + int n = 1 + rand() % 30; + string s(n, 0); + rep(i, 0, n) { + s[i] = char(rand() % alpha); + } + PalinTree tree(s); + map mp; + vector dp(n+1, vi(n+1)); + rep(i, 0, n+1) dp[0][i] = 1; + rep(l, 1, n + 1) rep(i, 0, n - l + 1) { + dp[l][i] = l<= 1 || (dp[l-2][i + 1] && s[i] == s[i + l - 1]); + if (dp[l][i]) + ++mp[s.substr(i, l)]; + } + rep(i, 2, tree.n) { + string sub = s.substr(tree.pos[i], tree.len[i]); + int cnt = mp.find(sub)->second; + assert(cnt == tree.freq[i]); + mp.erase(sub); + } + assert(mp.empty()); + } + } + cout << "Test passed!\n"; +} From ac039dd0af7bdb1ff4714d59da708feaf9d568fa Mon Sep 17 00:00:00 2001 From: Ahmed Dardery Date: Sat, 23 Oct 2021 14:02:54 +0200 Subject: [PATCH 2/2] use rep instead of for loop --- content/strings/PalindromicTree.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/strings/PalindromicTree.h b/content/strings/PalindromicTree.h index 34497244b..dc90aaac7 100644 --- a/content/strings/PalindromicTree.h +++ b/content/strings/PalindromicTree.h @@ -24,7 +24,7 @@ struct PalinTree { nxt.resize(sz(s) + 2); n = cur = fail[0] = fail[1] = 0; addNode(-1, -1), addNode(0, 0); - for(int i = 0; i < sz(s); ++i) addChar(i, s[i]); + rep(i, 0, sz(s)) addChar(i, s[i]); propagate(); } @@ -55,4 +55,4 @@ struct PalinTree { return u; } }; - \ No newline at end of file +