Skip to content

Commit 4295299

Browse files
yuuurchykYurii A.adamant-pwn
authored
Manacher's algorithm testcases (#1393)
* Fix inconsistencies in Manacher's algorithm * Add test for manacher_odd * Update test_manacher_odd.cpp * empty commit to run tests? --------- Co-authored-by: Yurii A. <l.soho@tuta.io> Co-authored-by: Oleksandr Kulkov <adamant.pwn@gmail.com>
1 parent b96e93b commit 4295299

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

src/string/manacher.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Such an algorithm is slow, it can calculate the answer only in $O(n^2)$.
4949
The implementation of the trivial algorithm is:
5050

5151
```cpp
52-
vector<int> manacher_odd(string s) {
52+
vector<int> manacher_odd_trivial(string s) {
5353
int n = s.size();
5454
s = "$" + s + "^";
5555
vector<int> p(n + 2);
@@ -68,7 +68,7 @@ Terminal characters `$` and `^` were used to avoid dealing with ends of the stri
6868
6969
We describe the algorithm to find all the sub-palindromes with odd length, i. e. to calculate $d_{odd}[]$.
7070
71-
For fast calculation we'll maintain the **borders $(l, r)$** of the rightmost found (sub-)palindrome (i. e. the current rightmost (sub-)palindrome is $s[l+1] s[l+2] \dots s[r-1]$). Initially we set $l = 0, r = 1$, which corresponds to the empty string.
71+
For fast calculation we'll maintain the **exclusive borders $(l, r)$** of the rightmost found (sub-)palindrome (i. e. the current rightmost (sub-)palindrome is $s[l+1] s[l+2] \dots s[r-1]$). Initially we set $l = 0, r = 1$, which corresponds to the empty string.
7272
7373
So, we want to calculate $d_{odd}[i]$ for the next $i$, and all the previous values in $d_{odd}[]$ have been already calculated. We do the following:
7474
@@ -140,12 +140,12 @@ For calculating $d_{odd}[]$, we get the following code. Things to note:
140140
- The while loop denotes the trivial algorithm. We launch it irrespective of the value of $k$.
141141
- If the size of palindrome centered at $i$ is $x$, then $d_{odd}[i]$ stores $\frac{x+1}{2}$.
142142
143-
```cpp
143+
```{.cpp file=manacher_odd}
144144
vector<int> manacher_odd(string s) {
145145
int n = s.size();
146146
s = "$" + s + "^";
147147
vector<int> p(n + 2);
148-
int l = 1, r = 1;
148+
int l = 0, r = 1;
149149
for(int i = 1; i <= n; i++) {
150150
p[i] = max(0, min(r - i, p[l + (r - i)]));
151151
while(s[i - p[i]] == s[i + p[i]]) {

test/test_manacher_odd.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <bits/stdc++.h>
2+
using namespace std;
3+
4+
#include "manacher_odd.h"
5+
6+
string getRandomString(size_t n, uint32_t seed, char minLetter='a', char maxLetter='b') {
7+
assert(minLetter <= maxLetter);
8+
const size_t nLetters = static_cast<int>(maxLetter) - static_cast<int>(minLetter) + 1;
9+
std::uniform_int_distribution<size_t> distr(0, nLetters - 1);
10+
std::mt19937 gen(seed);
11+
12+
string res;
13+
res.reserve(n);
14+
15+
for (size_t i = 0; i < n; ++i)
16+
res.push_back('a' + distr(gen));
17+
18+
return res;
19+
}
20+
21+
bool testManacherOdd(const std::string &s) {
22+
const auto n = s.size();
23+
const auto d_odd = manacher_odd(s);
24+
25+
if (d_odd.size() != n)
26+
return false;
27+
28+
const auto inRange = [&](size_t idx) {
29+
return idx >= 0 && idx < n;
30+
};
31+
32+
for (size_t i = 0; i < n; ++i) {
33+
if (d_odd[i] < 0)
34+
return false;
35+
for (int d = 0; d < d_odd[i]; ++d) {
36+
const auto idx1 = i - d;
37+
const auto idx2 = i + d;
38+
39+
if (!inRange(idx1) || !inRange(idx2))
40+
return false;
41+
if (s[idx1] != s[idx2])
42+
return false;
43+
}
44+
45+
const auto idx1 = i - d_odd[i];
46+
const auto idx2 = i + d_odd[i];
47+
if (inRange(idx1) && inRange(idx2) && s[idx1] == s[idx2])
48+
return false;
49+
}
50+
51+
return true;
52+
}
53+
54+
int main() {
55+
vector<string> testCases;
56+
57+
testCases.push_back("");
58+
for (size_t i = 1; i <= 25; ++i) {
59+
auto s = string{};
60+
s.resize(i, 'a');
61+
testCases.push_back(move(s));
62+
}
63+
testCases.push_back("abba");
64+
testCases.push_back("abccbaasd");
65+
for (size_t n = 9; n <= 100; n += 10)
66+
testCases.push_back(getRandomString(n, /* seed */ n, 'a', 'd'));
67+
for (size_t n = 7; n <= 100; n += 10)
68+
testCases.push_back(getRandomString(n, /* seed */ n));
69+
70+
for (const auto &s: testCases)
71+
assert(testManacherOdd(s));
72+
73+
return 0;
74+
}

0 commit comments

Comments
 (0)