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
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:
@@ -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} )
|
||||
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief
|
||||
* The
|
||||
* [Boyer–Moore](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),
|
||||
* Boyer–Moore 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief The [Knuth-Morris-Pratt
|
||||
* Algorithm](https://en.wikipedia.org/wiki/Knuth–Morris–Pratt_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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief The [Rabin-Karp
|
||||
* Algorithm](https://en.wikipedia.org/wiki/Rabin–Karp_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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user