chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:28 +08:00
commit 29cfe479ab
432 changed files with 68491 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
if(OpenMP_CXX_FOUND)
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
endif()
install(TARGETS ${testname} DESTINATION "bin/strings")
endforeach( testsourcefile ${APP_SOURCES} )
+272
View File
@@ -0,0 +1,272 @@
/**
* @file
* @brief
* The
* [BoyerMoore](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm)
* algorithm searches for occurrences of pattern P in text T by performing
* explicit character comparisons at different alignments. Instead of a
* brute-force search of all alignments (of which there are n - m + 1),
* BoyerMoore uses information gained by preprocessing P to skip as many
* alignments as possible.
*
* @details
* The key insight in this algorithm is that if the end of the pattern is
* compared to the text, then jumps along the text can be made rather than
* checking every character of the text. The reason that this works is that in
* lining up the pattern against the text, the last character of the pattern is
* compared to the character in the text.
*
* If the characters do not match, there is no need to continue searching
* backwards along the text. This leaves us with two cases.
*
* Case 1:
* If the character in the text does not match any of the characters in the
* pattern, then the next character in the text to check is located m characters
* farther along the text, where m is the length of the pattern.
*
* Case 2:
* If the character in the text is in the pattern, then a partial shift of the
* pattern along the text is done to line up along the matching character and
* the process is repeated.
*
* There are two shift rules:
*
* [The bad character rule]
* (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm#The_bad_character_rule)
*
* [The good suffix rule]
* (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm#The_good_suffix_rule)
*
* The shift rules are implemented as constant-time table lookups, using tables
* generated during the preprocessing of P.
* @author [Stoycho Kyosev](https://github.com/stoychoX)
*/
#include <cassert> /// for assert
#include <climits> /// for CHAR_MAX macro
#include <cstring> /// for strlen
#include <iostream> /// for IO operations
#include <string> /// for std::string
#include <vector> /// for std::vector
#define APLHABET_SIZE CHAR_MAX ///< number of symbols in the alphabet we use
/**
* @namespace
* @brief String algorithms
*/
namespace strings {
/**
* @namespace
* @brief Functions for the [Boyer
* Moore](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm)
* algorithm implementation
*/
namespace boyer_moore {
/**
* @brief A structure representing all the data we need to search the
* preprocessed pattern in text.
*/
struct pattern {
std::string pat;
std::vector<size_t>
bad_char; ///< bad char table used in [Bad Character
///< Heuristic](https://www.geeksforgeeks.org/boyer-moore-algorithm-for-pattern-searching/)
std::vector<size_t>
good_suffix; ///< good suffix table used for [Good Suffix
///< heuristic](https://www.geeksforgeeks.org/boyer-moore-algorithm-good-suffix-heuristic/?ref=rp)
};
/**
* @brief A function that preprocess the good suffix thable
*
* @param str The string being preprocessed
* @param arg The good suffix table
* @returns void
*/
void init_good_suffix(const std::string& str, std::vector<size_t>& arg) {
arg.resize(str.size() + 1, 0);
// border_pos[i] - the index of the longest proper suffix of str[i..] which
// is also a proper prefix.
std::vector<size_t> border_pos(str.size() + 1, 0);
size_t current_char = str.length();
size_t border_index = str.length() + 1;
border_pos[current_char] = border_index;
while (current_char > 0) {
while (border_index <= str.length() &&
str[current_char - 1] != str[border_index - 1]) {
if (arg[border_index] == 0) {
arg[border_index] = border_index - current_char;
}
border_index = border_pos[border_index];
}
current_char--;
border_index--;
border_pos[current_char] = border_index;
}
size_t largest_border_index = border_pos[0];
for (size_t i = 0; i < str.size(); i++) {
if (arg[i] == 0) {
arg[i] = largest_border_index;
}
// If we go pass the largest border we find the next one as we iterate
if (i == largest_border_index) {
largest_border_index = border_pos[largest_border_index];
}
}
}
/**
* @brief A function that preprocess the bad char table
*
* @param str The string being preprocessed
* @param arg The bad char table
* @returns void
*/
void init_bad_char(const std::string& str, std::vector<size_t>& arg) {
arg.resize(APLHABET_SIZE, str.length());
for (size_t i = 0; i < str.length(); i++) {
arg[str[i]] = str.length() - i - 1;
}
}
/**
* @brief A function that initializes pattern
*
* @param str Text used for initialization
* @param arg Initialized structure
* @returns void
*/
void init_pattern(const std::string& str, pattern& arg) {
arg.pat = str;
init_bad_char(str, arg.bad_char);
init_good_suffix(str, arg.good_suffix);
}
/**
* @brief A function that implements Boyer-Moore's algorithm.
*
* @param str Text we are seatching in.
* @param arg pattern structure containing the preprocessed pattern
* @return Vector of indexes of the occurrences of pattern in text
*/
std::vector<size_t> search(const std::string& str, const pattern& arg) {
size_t index_position = arg.pat.size() - 1;
std::vector<size_t> index_storage;
while (index_position < str.length()) {
size_t index_string = index_position;
int index_pattern = static_cast<int>(arg.pat.size()) - 1;
while (index_pattern >= 0 &&
str[index_string] == arg.pat[index_pattern]) {
--index_pattern;
--index_string;
}
if (index_pattern < 0) {
index_storage.push_back(index_position - arg.pat.length() + 1);
index_position += arg.good_suffix[0];
} else {
index_position += std::max(arg.bad_char[str[index_string]],
arg.good_suffix[index_pattern + 1]);
}
}
return index_storage;
}
/**
* @brief Check if pat is prefix of str.
*
* @param str pointer to some part of the input text.
* @param pat the searched pattern.
* @param len length of the searched pattern
* @returns `true` if pat IS prefix of str.
* @returns `false` if pat is NOT a prefix of str.
*/
bool is_prefix(const char* str, const char* pat, size_t len) {
if (strlen(str) < len) {
return false;
}
for (size_t i = 0; i < len; i++) {
if (str[i] != pat[i]) {
return false;
}
}
return true;
}
} // namespace boyer_moore
} // namespace strings
/**
* @brief A test case in which we search for every appearance of the word 'and'
* @param text The text in which we search for appearance of the word 'and'
* @returns void
*/
void and_test(const char* text) {
strings::boyer_moore::pattern ands;
strings::boyer_moore::init_pattern("and", ands);
std::vector<size_t> indexes = strings::boyer_moore::search(text, ands);
assert(indexes.size() == 2);
assert(strings::boyer_moore::is_prefix(text + indexes[0], "and", 3));
assert(strings::boyer_moore::is_prefix(text + indexes[1], "and", 3));
}
/**
* @brief A test case in which we search for every appearance of the word 'pat'
* @param text The text in which we search for appearance of the word 'pat'
* @returns void
*/
void pat_test(const char* text) {
strings::boyer_moore::pattern pat;
strings::boyer_moore::init_pattern("pat", pat);
std::vector<size_t> indexes = strings::boyer_moore::search(text, pat);
assert(indexes.size() == 6);
for (const auto& currentIndex : indexes) {
assert(strings::boyer_moore::is_prefix(text + currentIndex, "pat", 3));
}
}
/**
* @brief Self-test implementations
* @returns void
*/
static void tests() {
const char* text =
"When pat Mr. and Mrs. pat Dursley woke up on the dull, gray \
Tuesday our story starts, \
there was nothing about pat the cloudy sky outside to pat suggest that\
strange and \
mysterious things would pat soon be happening all pat over the \
country.";
and_test(text);
pat_test(text);
std::cout << "All tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
tests(); // run self-test implementations
return 0;
}
+58
View File
@@ -0,0 +1,58 @@
/**
* @file
* @brief String pattern search - brute force
*/
#include <iostream>
#ifdef _MSC_VER
#include <string> // use this for MS Visual C++
#else
#include <cstring>
#endif
#include <vector>
namespace string_search {
/**
* Find a pattern in a string by comparing the pattern to every substring.
* @param text Any string that might contain the pattern.
* @param pattern String that we are searching for.
* @return Index where the pattern starts in the text
* @return -1 if the pattern was not found.
*/
int brute_force(const std::string &text, const std::string &pattern) {
size_t pat_l = pattern.length();
size_t txt_l = text.length();
int index = -1;
if (pat_l <= txt_l) {
for (size_t i = 0; i < txt_l - pat_l + 1; i++) {
std::string s = text.substr(i, pat_l);
if (s == pattern) {
index = i;
break;
}
}
}
return index;
}
} // namespace string_search
using string_search::brute_force;
/** set of test cases */
const std::vector<std::vector<std::string>> test_set = {
// {text, pattern, expected output}
{"a", "aa", "-1"}, {"a", "a", "0"}, {"ba", "b", "0"},
{"bba", "bb", "0"}, {"bbca", "c", "2"}, {"ab", "b", "1"}};
/** Main function */
int main() {
for (const auto &i : test_set) {
int output = brute_force(i[0], i[1]);
if (std::to_string(output) == i[2]) {
std::cout << "success\n";
} else {
std::cout << "failure\n";
}
}
return 0;
}
+118
View File
@@ -0,0 +1,118 @@
/**
* @file duval.cpp
* @brief Implementation of [Duval's algorithm](https://en.wikipedia.org/wiki/Lyndon_word).
*
* @details
* Duval's algorithm is an algorithm to find the lexicographically smallest
* rotation of a string. It is based on the concept of Lyndon words.
* Lyndon words are defined as the lexicographically smallest string in a
* rotation equivalence class. A rotation equivalence class is a set of strings
* that can be obtained by rotating a string. For example, the rotation
* equivalence class of "abc" is {"abc", "bca", "cab"}. The lexicographically
* smallest string in this class is "abc".
*
* Duval's algorithm works by iterating over the string and finding the
* smallest rotation of the string that is a Lyndon word. This is done by
* comparing the string with its suffixes and finding the smallest suffix that
* is lexicographically smaller than the string. This suffix is then added to
* the result and the process is repeated with the remaining string.
* The algorithm has a time complexity of O(n) where n is the length of the
* string.
*
* @note While Lyndon words are described in the context of strings,
* Duval's algorithm can be used to find the lexicographically smallest cyclic
* shift of any sequence of comparable elements.
*
* @author [Amine Ghoussaini](https://github.com/aminegh20)
*/
#include <array> /// for std::array
#include <cassert> /// for assert
#include <cstddef> /// for std::size_t
#include <deque> /// for std::deque
#include <iostream> /// for std::cout and std::endl
#include <string> /// for std::string
#include <vector> /// for std::vector
/**
* @brief string manipulation algorithms
* @namespace
*/
namespace string {
/**
* @brief Find the lexicographically smallest cyclic shift of a sequence.
* @tparam T type of the sequence
* @param s the sequence
* @returns the 0-indexed position of the least cyclic shift of the sequence
*/
template <typename T>
size_t duval(const T& s) {
size_t n = s.size();
size_t i = 0, ans = 0;
while (i < n) {
ans = i;
size_t j = i + 1, k = i;
while (j < (n + n) && s[j % n] >= s[k % n]) {
if (s[k % n] < s[j % n]) {
k = i;
} else {
k++;
}
j++;
}
while (i <= k) {
i += j - k;
}
}
return ans;
// returns 0-indexed position of the least cyclic shift
}
} // namespace string
/**
* @brief self test implementation
* returns void
*/
static void test() {
using namespace string;
// Test 1
std::string s1 = "abcab";
assert(duval(s1) == 3);
// Test 2
std::string s2 = "011100";
assert(duval(s2) == 4);
// Test 3
std::vector<int> v = {5, 2, 1, 3, 4};
assert(duval(v) == 2);
// Test 4
std::array<int, 5> a = {1, 2, 3, 4, 5};
assert(duval(a) == 0);
// Test 5
std::deque<char> d = {'a', 'z', 'c', 'a', 'b'};
assert(duval(d) == 3);
// Test 6
std::string s3;
assert(duval(s3) == 0);
// Test 7
std::vector<int> v2 = {5, 2, 1, 3, -4};
assert(duval(v2) == 4);
std::cout << "All tests passed!" << std::endl;
}
/**
* @brief main function
* @returns 0 on exit
*/
int main() {
test(); // run self test implementations
return 0;
}
+122
View File
@@ -0,0 +1,122 @@
/**
* @file
* @brief Horspool's algorithm that finds if a string contains a substring (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm)
* @author [Harry Kontakis](https://github.com/ckontakis)
*/
#include <iostream>
#include <unordered_map>
#include <cassert>
/**
* @namespace strings
* @brief Algorithms with strings
*/
namespace strings {
/**
* @namespace horspool
* @brief Functions for [Horspool's](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm) algorithm
*/
namespace horspool {
/**
* A function that finds the shift table of the given prototype string that we need in Horpool's algorithm.
* @param prototype is the substring that we use to find shift table
* @return Shift Table of Horspool's algorithm
*/
std::unordered_map<char, int> findShiftTable(const std::string &prototype) {
std::unordered_map<char, int>
shiftTable; // A HashMap for shift table that has characters for keys and integers for values
for (int i = 0; i < prototype.size();
i++) { // Checking all characters of prototype string
if (shiftTable.find(prototype[i]) ==
shiftTable.end()) { // If character does not exist in HashMap
if (i != prototype.size() - 1) {
shiftTable.insert(std::make_pair(
prototype[i], prototype.size() - i -
1)); // Insert the character as key and the size of prototype string - index of character - 1 as value
} else {
shiftTable.insert(std::make_pair(
prototype[i],
prototype.size())); // Insert the character as key and the size of prototype string as value
}
} else {
if (i != prototype.size() - 1) {
shiftTable[prototype[i]] = prototype.size() - i - 1;
}
}
}
return shiftTable;
}
/**
* A function that implements Horspool's algorithm.
* @param text is the string that we are searching if there is a substring
* @param prototype is the substring that we are searching in text
* @returns true if text string contains prototype string
* @returns false if text string does not contain prototype string
*/
bool horspool(const std::string &text, const std::string &prototype) {
std::unordered_map<char, int> shiftTable = findShiftTable(
prototype); // Initialise shift table calling findShiftTable function
int i = static_cast<int>(
prototype.size() -
1); // Index that we shift in text to find the substring
while (i < text.size()) {
int j = i, k = 0;
bool flag = true;
for (int z = static_cast<int>(prototype.size() - 1); z >= 0 && flag;
z--) { // Checking if all characters of substring are equal with all characters of string
if (text[j] == prototype[z]) {
k++;
j--;
} else {
flag = false; // If two characters are not equal set flag to false and break from loop
}
}
if (k ==
prototype.size()) { // If all characters match then return true
return true;
} else {
if (shiftTable.find(text[i]) != shiftTable.end()) {
i += shiftTable[text[i]]; // If shift table contains the character then shift index as many steps as value
} else {
i += prototype.size(); // If character does not exist in shift table then shift index as many steps as size of prototype string
}
}
}
return false;
}
} // namespace horspool
} // namespace strings
/**
* @brief Function with test cases for Horspool's algorithm
* @returns void
*/
static void test(){
assert(strings::horspool::horspool("Hello World","World") == true);
assert(strings::horspool::horspool("Hello World"," World") == true);
assert(strings::horspool::horspool("Hello World","ello") == true);
assert(strings::horspool::horspool("Hello World","rld") == true);
assert(strings::horspool::horspool("Hello","Helo") == false);
assert(strings::horspool::horspool("c++_algorithms","c++_algorithms") == true);
assert(strings::horspool::horspool("c++_algorithms","c++_") == true);
assert(strings::horspool::horspool("Hello","Hello World") == false);
assert(strings::horspool::horspool("c++_algorithms","") == false);
assert(strings::horspool::horspool("c++","c") == true);
assert(strings::horspool::horspool("3458934793","4793") == true);
assert(strings::horspool::horspool("3458934793","123") == false);
}
/**
* @brief Main Function that calls test function
* @returns 0 on exit
*/
int main(){
test();
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
/**
* @file
* @brief The [Knuth-Morris-Pratt
* Algorithm](https://en.wikipedia.org/wiki/KnuthMorrisPratt_algorithm) for
* finding a pattern within a piece of text with complexity O(n + m)
* @details
* 1. Preprocess pattern to identify any suffixes that are identical to
* prefixes. This tells us where to continue from if we get a mismatch between a
* character in our pattern and the text.
* 2. Step through the text one character at a time and compare it to a
* character in the pattern updating our location within the pattern if
* necessary
* @author [Yancey](https://github.com/Yancey2023)
*/
#include <cassert> /// for assert
#include <iostream> /// for IO operations
#include <string> /// for std::string
#include <vector> /// for std::vector
/**
* @namespace string_search
* @brief String search algorithms
*/
namespace string_search {
/**
* @brief Generate the partial match table aka failure function for a pattern to
* search.
* @param pattern text for which to create the partial match table
* @returns the partial match table as a vector array
*/
std::vector<size_t> getFailureArray(const std::string &pattern) {
size_t pattern_length = pattern.size();
std::vector<size_t> failure(pattern_length + 1);
failure[0] = std::string::npos;
size_t j = std::string::npos;
for (int i = 0; i < pattern_length; i++) {
while (j != std::string::npos && pattern[j] != pattern[i]) {
j = failure[j];
}
failure[i + 1] = ++j;
}
return failure;
}
/**
* @brief KMP algorithm to find a pattern in a text
* @param pattern string pattern to search
* @param text text in which to search
* @returns the starting index of the pattern if found
* @returns `std::string::npos` if not found
*/
size_t kmp(const std::string &pattern, const std::string &text) {
if (pattern.empty()) {
return 0;
}
std::vector<size_t> failure = getFailureArray(pattern);
size_t text_length = text.size();
size_t pattern_length = pattern.size();
size_t k = 0;
for (size_t j = 0; j < text_length; j++) {
while (k != std::string::npos && pattern[k] != text[j]) {
k = failure[k];
}
if (++k == pattern_length) {
return j - k + 1;
}
}
return std::string::npos;
}
} // namespace string_search
using string_search::kmp;
/**
* @brief self-test implementations
* @returns void
*/
static void tests() {
assert(kmp("abc1abc12l", "alskfjaldsabc1abc1abc12k2") == std::string::npos);
assert(kmp("bca", "abcabc") == 1);
assert(kmp("World", "helloWorld") == 5);
assert(kmp("c++", "his_is_c++") == 7);
assert(kmp("happy", "happy_coding") == 0);
assert(kmp("", "pattern is empty") == 0);
// this lets the user know that the tests have passed
std::cout << "All KMP algorithm tests have successfully passed!\n";
}
/*
* @brief Main function
* @returns 0 on exit
*/
int main() {
tests();
return 0;
}
+175
View File
@@ -0,0 +1,175 @@
/**
* @file
* @brief Implementation of [Manacher's
* Algorithm](https://en.wikipedia.org/wiki/Longest_palindromic_substring)
* @details
* Manacher's Algorithm is used to find the longest palindromic substring within
* a string in O(n) time. It exploits the property of a palindrome that its
* first half is symmetric to the last half, and thus if the first half is a
* palindrome, then last half is also a palindrome.
* @author [Riti Kumari](https://github.com/riti2409)
*/
#include <cassert> /// for assert
#include <cstdint>
#include <iostream> /// for IO operations
#include <vector> /// for std::vector STL
#ifdef _MSC_VER
#include <string> /// for string (required for MS Visual C++)
#else
#include <cstring> /// for string
#endif
/**
* @namespace strings
* @brief Algorithms with strings
*/
namespace strings {
/**
* @namespace manacher
* @brief Functions for [Manacher's
* Algorithm](https://en.wikipedia.org/wiki/Longest_palindromic_substring)
* implementation
*/
namespace manacher {
/**
* @brief A function that implements Manacher's algorithm
* @param prototype is the string where algorithm finds a palindromic substring.
* This string can contain any character except `@` `#` `&`
* @returns the largest palindromic substring
*/
std::string manacher(const std::string &prototype) {
if (prototype.size() > 0) {
// stuffing characters between the input string to handle cases with
// even length palindrome
std::string stuffed_string = "";
for (auto str : prototype) {
stuffed_string += str;
stuffed_string += "#";
}
stuffed_string = "@#" + stuffed_string + "&";
std::vector<uint64_t> palindrome_max_half_length(
stuffed_string.size(),
0); // this array will consist of largest possible half length of
// palindrome centered at index (say i with respect to the
// stuffed string). This value will be lower bound of half
// length since single character is a palindrome in itself.
uint64_t bigger_center =
0; // this is the index of the center of palindromic
// substring which would be considered as the larger
// palindrome, having symmetric halves
uint64_t right = 0; // this is the maximum length of the palindrome
// from 'bigger_center' to the rightmost end
// i is considered as center lying within one half of the palindrone
// which is centered at 'bigger_center'
for (uint64_t i = 1; i < stuffed_string.size() - 1; i++) {
if (i < right) { // when i is before right end, considering
// 'bigger_center' as center of palindrome
uint64_t opposite_to_i =
2 * bigger_center -
i; // this is the opposite end of string, if
// centered at center, and having one end as i
// finding the minimum possible half length among
// the palindrome on having center at opposite end,
// and the string between i and right end,
// considering 'bigger_center' as center of palindrome
palindrome_max_half_length[i] = std::min(
palindrome_max_half_length[opposite_to_i], right - i);
}
// expanding the palindrome across the maximum stored length in the
// array, centered at i
while (stuffed_string[i + (palindrome_max_half_length[i] + 1)] ==
stuffed_string[i - (palindrome_max_half_length[i] + 1)]) {
palindrome_max_half_length[i]++;
}
// if palindrome centered at i exceeds the rightmost end of
// palindrome centered at 'bigger_center', then i will be made the
// 'bigger_center' and right value will also be updated with respect
// to center i
if (i + palindrome_max_half_length[i] > right) {
bigger_center = i;
right = i + palindrome_max_half_length[i];
}
}
// now extracting the first largest palindrome
uint64_t half_length = 0; // half length of the largest palindrome
uint64_t center_index = 0; // index of center of the largest palindrome
for (uint64_t i = 1; i < stuffed_string.size() - 1; i++) {
if (palindrome_max_half_length[i] > half_length) {
half_length = palindrome_max_half_length[i];
center_index = i;
}
}
std::string palindromic_substring =
""; // contains the resulting largest palindrome
if (half_length > 0) {
// extra information: when '#' is the center, then palindromic
// substring will have even length, else palindromic substring will
// have odd length
uint64_t start =
center_index - half_length +
1; // index of first character of palindromic substring
uint64_t end =
center_index + half_length -
1; // index of last character of palindromic substring
for (uint64_t index = start; index <= end; index += 2) {
palindromic_substring += stuffed_string[index];
}
} else {
// if length = 0, then there does not exist any palindrome of length
// > 1 so we can assign any character of length 1 from string as the
// palindromic substring
palindromic_substring = prototype[0];
}
return palindromic_substring;
} else {
// handling case when string is empty
return "";
}
}
} // namespace manacher
} // namespace strings
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
assert(strings::manacher::manacher("") == "");
assert(strings::manacher::manacher("abababc") == "ababa");
assert(strings::manacher::manacher("cbaabd") == "baab");
assert(strings::manacher::manacher("DedzefDeD") == "DeD");
assert(strings::manacher::manacher("XZYYXXYZXX") == "YXXY");
assert(strings::manacher::manacher("1sm222m10abc") == "m222m");
assert(strings::manacher::manacher("798989591") == "98989");
assert(strings::manacher::manacher("xacdedcax") == "xacdedcax");
assert(strings::manacher::manacher("xaccax") == "xaccax");
assert(strings::manacher::manacher("a") == "a");
assert(strings::manacher::manacher("xy") == "x");
assert(strings::manacher::manacher("abced") == "a");
std::cout << "All tests have passed!" << std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+111
View File
@@ -0,0 +1,111 @@
/**
* \file
* \brief The [Rabin-Karp
* Algorithm](https://en.wikipedia.org/wiki/RabinKarp_algorithm) for finding a
* pattern within a piece of text with complexity O(n + m)
*/
#include <cassert>
#include <cmath>
#include <iostream>
#ifdef _MSC_VER
#include <string> // use this for MS Visucal C++
#else
#include <cstring>
#endif
#define PRIME 5 ///< Prime modulus for hash functions
namespace string_search {
/**
* convert a string to an intger - called as hashing function
* \param[in] s source of string to hash
* \param[in] n length of substring to hash
* \returns hash integer
*/
int64_t create_hash(const std::string& s, int n) {
int64_t result = 0;
for (int i = 0; i < n; ++i) {
result += (int64_t)(s[i] * (int64_t)pow(PRIME, i));
}
return result;
}
/**
* re-hash a string using known existing hash
* \param[in] s source of string to hash
* \param[in] old_index previous index of string
* \param[in] new_index new index of string
* \param[in] old_hash previous hash of substring
* \param[in] patLength length of substring to hash
* \returns new hash integer
*/
int64_t recalculate_hash(const std::string& s, int old_index, int new_index,
int64_t old_hash, int patLength) {
int64_t new_hash = old_hash - s[old_index];
new_hash /= PRIME;
new_hash += (int64_t)(s[new_index] * (int64_t)pow(PRIME, patLength - 1));
return new_hash;
}
/**
* compare if two sub-strings are equal
* \param[in] str1 string pattern to search
* \param[in] str2 text in which to search
* \param[in] start1,end1 start and end indices for substring in str1
* \param[in] start2,end2 start and end indices for substring in str2
* \returns `true` if pattern was found
* \returns `false` if pattern was not found
* @note can this be replaced by std::string::compare?
*/
bool check_if_equal(const std::string& str1, const std::string& str2,
int start1, int end1, int start2, int end2) {
if (end1 - start1 != end2 - start2) {
return false;
}
while (start1 <= end1 && start2 <= end2) {
if (str1[start1] != str2[start2]) {
return false;
}
start1++;
start2++;
}
return true;
}
/**
* Perform string pattern search using Rabin-Karp algorithm
* @param[in] str string to search in
* @param[in] pat pattern to search for
* @return index of first occurrence of pattern
* @return -1 if pattern not found
*/
int rabin_karp(const std::string& str, const std::string& pat) {
int64_t pat_hash = create_hash(pat, pat.size());
int64_t str_hash = create_hash(str, pat.size());
for (int i = 0; i <= str.size() - pat.size(); ++i) {
if (pat_hash == str_hash &&
check_if_equal(str, pat, i, i + pat.size() - 1, 0,
pat.size() - 1)) {
return i;
}
if (i < str.size() - pat.size()) {
str_hash =
recalculate_hash(str, i, i + pat.size(), str_hash, pat.size());
}
}
return -1; // return -1 if given pattern not found
}
} // namespace string_search
using string_search::rabin_karp;
/** Main function */
int main(void) {
assert(rabin_karp("helloWorld", "world") == -1);
assert(rabin_karp("helloWorld", "World") == 5);
assert(rabin_karp("this_is_c++", "c++") == 8);
assert(rabin_karp("happy_coding", "happy") == 0);
return 0;
}
+113
View File
@@ -0,0 +1,113 @@
/**
* @file
* @brief The [Z function](https://cp-algorithms.com/string/z-function.html) for
* finding occurences of a pattern within a piece of text with time and space
* complexity O(n + m)
* @details
* 1. The Z-function for a string is an array of length n where the
* i-th element is equal to the greatest number of characters starting
* from the position i that coincide with the first characters of s.
* 2. E.g.: string: ababb then z[2]=2 as s[2]=s[0] and s[3]=s[1] and s[4]!=s[2]
* @author [Ritika Gupta](https://github.com/RitikaGupta8734)
*/
#include <cstdint>
#include <iostream> /// for IO operations
#ifdef _MSC_VER
#include <string> /// for string (use this for MS Visual C++)
#else
#include <cstring> /// for string
#endif
#include <cassert> /// for assert
#include <vector> /// for std::vector
/**
* @brief Generate the Z-function for the inputted string.
* \param[in] pattern text on which to apply the Z-function
* \returns the Z-function output as a vector array
*/
std::vector<uint64_t> Z_function(const std::string &pattern) {
uint64_t pattern_length = pattern.size();
std::vector<uint64_t> z(pattern_length, 0);
for (uint64_t i = 1, l = 0, r = 0; i < pattern_length; i++) {
if (i <= r) {
z[i] = std::min(r - i + 1, z[i - l]);
}
while (i + z[i] < pattern_length &&
pattern[z[i]] == pattern[i + z[i]]) {
z[i]++;
}
if (i + z[i] - 1 > r) {
r = i + z[i] - 1;
}
}
return z;
}
/**
* @brief Using Z_function to find a pattern in a text
* \param[in] pattern string pattern to search
* \param[in] text text in which to search
* \returns a vector of starting indexes where pattern is found in the text
*/
std::vector<uint64_t> find_pat_in_text(const std::string &pattern,
const std::string &text) {
uint64_t text_length = text.size(), pattern_length = pattern.size();
std::vector<uint64_t> z = Z_function(pattern + '#' + text);
std::vector<uint64_t> matching_indexes;
for (uint64_t i = 0; i < text_length; i++) {
if (z[i + pattern_length + 1] == pattern_length) {
matching_indexes.push_back(i);
}
}
return matching_indexes;
}
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
// usual case
std::string text1 = "alskfjaldsabc1abc1abcbksbcdnsdabcabc";
std::string pattern1 = "abc";
// matching_indexes1 gets the indexes where pattern1 exists in text1
std::vector<uint64_t> matching_indexes1 = find_pat_in_text(pattern1, text1);
assert((matching_indexes1 == std::vector<uint64_t>{10, 14, 18, 30, 33}));
// corner case
std::string text2 = "greengrass";
std::string pattern2 = "abc";
// matching_indexes2 gets the indexes where pattern2 exists in text2
std::vector<uint64_t> matching_indexes2 = find_pat_in_text(pattern2, text2);
assert((matching_indexes2 == std::vector<uint64_t>{}));
// corner case - empty text
std::string text3 = "";
std::string pattern3 = "abc";
// matching_indexes3 gets the indexes where pattern3 exists in text3
std::vector<uint64_t> matching_indexes3 = find_pat_in_text(pattern3, text3);
assert((matching_indexes3 == std::vector<uint64_t>{}));
// corner case - empty pattern
std::string text4 = "redsand";
std::string pattern4 = "";
// matching_indexes4 gets the indexes where pattern4 exists in text4
std::vector<uint64_t> matching_indexes4 = find_pat_in_text(pattern4, text4);
assert((matching_indexes4 == std::vector<uint64_t>{0, 1, 2, 3, 4, 5, 6}));
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}