From fb97daa7dc351a6f591c8edd6cd46cff96637aea Mon Sep 17 00:00:00 2001 From: nikalosa Date: Wed, 3 Jun 2020 19:52:15 +0400 Subject: [PATCH 1/6] Add Z-function algorithm implementation --- strings/z_function.py | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 strings/z_function.py diff --git a/strings/z_function.py b/strings/z_function.py new file mode 100644 index 000000000000..2b7ab00f7f27 --- /dev/null +++ b/strings/z_function.py @@ -0,0 +1,61 @@ +""" +https://cp-algorithms.com/string/z-function.html#:~:text=The%20Z%2Dfunction%20for%20this,Note. + +For given string this algorithm computes value for each index, +which represents the maximal length substring starting from the index +and is same as the prefix of the same size + +e.x. for string 'abab' for second index value would be 2 + +The main adventage of the algoirthm is that it's linear, using dynamic programming + +Time Complexity: O(n) - where n is the length of the string + + +For the value of the first element the algorithm always returns 0 + +""" + + +def z_function(input_str: str) -> list: + """ + Will convert the entire string to uppercase letters + + >>> z_function("abracadabra") + [0, 0, 0, 1, 0, 1, 0, 4, 0, 0, 1] + >>> z_function("aaaa") + [0, 3, 2, 1] + >>> z_function("zxxzxxz") + [0, 0, 0, 4, 0, 0, 1] + + """ + + z_result = [0] * len(input_str) + + # initialize interval's left pointer and right pointer + left_pointer, right_pointer = 0, 0 + + for i in range(1, len(input_str)): + # case when current index is inside the interval + if i <= right_pointer: + min_edge = min(right_pointer - i + 1, z_result[i - left_pointer]) + z_result[i] = min_edge + + while go_next(i, z_result, input_str): + z_result[i] += 1 + + # if new index's result gives us more right interval, we've to update left_pointer and right_pointer + if i + z_result[i] - 1 > right_pointer: + left_pointer, right_pointer = i, i + z_result[i] - 1 + + return z_result + + +# helping function which checks if following elements are equal or not +def go_next(i, z_result, s): + return i + z_result[i] < len(s) and s[z_result[i]] == s[i + z_result[i]] + + +if __name__ == "__main__": + import doctest + doctest.testmod() From d2f4f052748c0104b12b3b71eb53b9fcf0ac2897 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Wed, 3 Jun 2020 19:58:28 +0400 Subject: [PATCH 2/6] Spelling correction --- strings/z_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/z_function.py b/strings/z_function.py index 2b7ab00f7f27..a4488248082c 100644 --- a/strings/z_function.py +++ b/strings/z_function.py @@ -7,7 +7,7 @@ e.x. for string 'abab' for second index value would be 2 -The main adventage of the algoirthm is that it's linear, using dynamic programming +The main adventage of the algorithm is that it's linear, using dynamic programming Time Complexity: O(n) - where n is the length of the string From 9d70fb872db3f0288609d28981fd1ae420f6ff06 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Wed, 3 Jun 2020 23:21:14 +0400 Subject: [PATCH 3/6] Reference url correction --- strings/z_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/z_function.py b/strings/z_function.py index a4488248082c..e0428fa8de50 100644 --- a/strings/z_function.py +++ b/strings/z_function.py @@ -1,5 +1,5 @@ """ -https://cp-algorithms.com/string/z-function.html#:~:text=The%20Z%2Dfunction%20for%20this,Note. +https://cp-algorithms.com/string/z-function.html For given string this algorithm computes value for each index, which represents the maximal length substring starting from the index From 409521c0cc5639b86ee9a50fdde668e60ff9efd8 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Thu, 11 Jun 2020 20:55:29 +0400 Subject: [PATCH 4/6] Add additional function as an example of z-function usage, change docstrings for functions --- strings/z_function.py | 56 +++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/strings/z_function.py b/strings/z_function.py index e0428fa8de50..7697e1886fa3 100644 --- a/strings/z_function.py +++ b/strings/z_function.py @@ -1,25 +1,24 @@ """ https://cp-algorithms.com/string/z-function.html -For given string this algorithm computes value for each index, -which represents the maximal length substring starting from the index -and is same as the prefix of the same size +Z-function or Z algorithm -e.x. for string 'abab' for second index value would be 2 - -The main adventage of the algorithm is that it's linear, using dynamic programming +Efficient algorithm for pattern occurrence in a string Time Complexity: O(n) - where n is the length of the string - -For the value of the first element the algorithm always returns 0 - """ def z_function(input_str: str) -> list: """ - Will convert the entire string to uppercase letters + For given string this function computes value for each index, + which represents the maximal length substring starting from the index + and is same as the prefix of the same size + + e.x. for string 'abab' for second index value would be 2 + + For the value of the first element the algorithm always returns 0 >>> z_function("abracadabra") [0, 0, 0, 1, 0, 1, 0, 4, 0, 0, 1] @@ -44,18 +43,51 @@ def z_function(input_str: str) -> list: while go_next(i, z_result, input_str): z_result[i] += 1 - # if new index's result gives us more right interval, we've to update left_pointer and right_pointer + # if new index's result gives us more right interval, + # we've to update left_pointer and right_pointer if i + z_result[i] - 1 > right_pointer: left_pointer, right_pointer = i, i + z_result[i] - 1 return z_result -# helping function which checks if following elements are equal or not def go_next(i, z_result, s): + """ + Helping function, which checks if we have to + move forward to the next characters or not + """ return i + z_result[i] < len(s) and s[z_result[i]] == s[i + z_result[i]] +def find_pattern(pattern: str, input_str: str) -> int: + """ + Example of using z-function for pattern occurrence + Given function returns the number of times 'pattern' + appears in 'input_str' as a substring + + >>> find_pattern("abr", "abracadabra") + 2 + >>> find_pattern("a", "aaaa") + 4 + >>> find_pattern("xz", "zxxzxxz") + 2 + + """ + answer = 0 + # concatenate 'pattern' and 'input_str' and call z_function + # with concatenated string + z_result = z_function(pattern + input_str) + + for val in z_result: + # if value is greater then length of the pattern string + # that means this index is starting position of substring + # which is equal to pattern string + if val >= len(pattern): + answer += 1 + + return answer + + if __name__ == "__main__": import doctest doctest.testmod() From c8028841e21b457049a9e6ab7f2c95dace78de39 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Thu, 11 Jun 2020 21:16:04 +0400 Subject: [PATCH 5/6] Fix flake8 errors --- strings/z_function.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/strings/z_function.py b/strings/z_function.py index 7697e1886fa3..4aac23b36728 100644 --- a/strings/z_function.py +++ b/strings/z_function.py @@ -5,16 +5,16 @@ Efficient algorithm for pattern occurrence in a string -Time Complexity: O(n) - where n is the length of the string +Time Complexity: O(n) - where n is the length of the string """ def z_function(input_str: str) -> list: """ - For given string this function computes value for each index, + For the given string this function computes value for each index, which represents the maximal length substring starting from the index - and is same as the prefix of the same size + and is the same as the prefix of the same size e.x. for string 'abab' for second index value would be 2 @@ -62,7 +62,7 @@ def go_next(i, z_result, s): def find_pattern(pattern: str, input_str: str) -> int: """ Example of using z-function for pattern occurrence - Given function returns the number of times 'pattern' + Given function returns the number of times 'pattern' appears in 'input_str' as a substring >>> find_pattern("abr", "abracadabra") @@ -71,7 +71,6 @@ def find_pattern(pattern: str, input_str: str) -> int: 4 >>> find_pattern("xz", "zxxzxxz") 2 - """ answer = 0 # concatenate 'pattern' and 'input_str' and call z_function From 4ae76148e0571067fec07dccf0b4dd992336c3d9 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 11 Jun 2020 19:42:12 +0200 Subject: [PATCH 6/6] Update z_function.py --- strings/z_function.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/strings/z_function.py b/strings/z_function.py index 4aac23b36728..55be81935cc5 100644 --- a/strings/z_function.py +++ b/strings/z_function.py @@ -26,9 +26,7 @@ def z_function(input_str: str) -> list: [0, 3, 2, 1] >>> z_function("zxxzxxz") [0, 0, 0, 4, 0, 0, 1] - """ - z_result = [0] * len(input_str) # initialize interval's left pointer and right pointer @@ -53,8 +51,7 @@ def z_function(input_str: str) -> list: def go_next(i, z_result, s): """ - Helping function, which checks if we have to - move forward to the next characters or not + Check if we have to move forward to the next characters or not """ return i + z_result[i] < len(s) and s[z_result[i]] == s[i + z_result[i]]