Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Coding Interview
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Container registry
Model registry
Analyze
Contributor analytics
Repository analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Dmytro Bogatov
Coding Interview
Commits
b318683f
Verified
Commit
b318683f
authored
Jun 5, 2020
by
Dmytro Bogatov
Browse files
Options
Downloads
Patches
Plain Diff
Solve Find Strings.
parent
fff66861
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/hacker-rank/FindStrings.cs
+181
-0
181 additions, 0 deletions
src/hacker-rank/FindStrings.cs
test/hacker-rank/FindStrings.cs
+23
-0
23 additions, 0 deletions
test/hacker-rank/FindStrings.cs
with
204 additions
and
0 deletions
src/hacker-rank/FindStrings.cs
0 → 100644
+
181
−
0
View file @
b318683f
using
System.Collections.Generic
;
using
System.Linq
;
namespace
CodingInterview.HackerRank
{
/// <summary>
/// A substring is defined as a contiguous sequence of one or more characters in the string.
/// More information on substrings can be found here (https://en.wikipedia.org/wiki/Substring).
///
/// You are given n strings w[1], w[2], ... , w[n].
/// Let S[i] denote the set of all unique substrings of the string w[i].
/// Let S = { S[1] U S[2] ... U S[n] }, that is, S is a set of strings that is the union of all substrings in all sets S[1], S[2], ... , S[n].
/// There will be many queries.
/// For each query you will be given an integer k.
/// Your task is to find the k_th element of the 1-indexed lexicographically ordered set of substrings in the set S.
/// If there is no element k, return INVALID.
///
/// For example, your strings are w = [abc, cde].
/// All of the substrings are S[1] = { a, b, c, ab, bc, abc } and S[2] = { c, d, e, cd, de, cde }.
/// Combine the two sets and sort them to get S = [ a, ab, abc, b, bc, c, cd, cde, d, de, e ].
/// So, for instance if k = 1, we return 'a'.
/// If k = 5, we return 'bc'.
/// If k = 20 though, there is not an S[20] so we return INVALID.
///
/// https://www.hackerrank.com/challenges/find-strings/problem
/// </summary>
/// <remark>
/// This solution somehow does not pass all HackerRank tests.
/// It shows "runtime error", but without tests I was not able to reproduce.
/// </remark>
public
class
FindStrings
{
class
PrefixTree
{
class
Node
{
public
string
Value
{
get
;
set
;
}
// the number of elements in the the subtree defined by this node;
// this includes this node's value
public
int
SubtreeSize
{
get
;
set
;
}
// parent link for upward traversal
public
Node
Parent
{
get
;
set
;
}
// lexicographically ordered list of children
public
List
<
Node
>
Children
{
get
;
set
;
}
=
new
List
<
Node
>();
}
// the empty root
private
Node
_root
;
public
PrefixTree
()
{
_root
=
new
Node
{
Value
=
""
};
}
public
string
Query
(
int
query
)
{
query
++;
// we count empty root as 1
// we know a priori if the query results in INVALID
if
(
_root
.
SubtreeSize
<
query
||
query
<
2
)
{
return
"INVALID"
;
}
// iterative traversal (somewhat easier than the recursive)
var
thisNode
=
_root
;
while
(
true
)
{
// return the value if query asks for it
if
(
query
==
1
)
{
return
thisNode
.
Value
;
}
// traverse children
foreach
(
var
child
in
thisNode
.
Children
)
{
// decide if to skip the child go into it
if
(
child
.
SubtreeSize
<
query
-
1
)
{
query
-=
child
.
SubtreeSize
;
}
else
{
thisNode
=
child
;
query
-=
1
;
// remember to count this value
break
;
}
}
}
}
public
void
Add
(
string
@new
)
{
var
thisNode
=
_root
;
while
(
true
)
{
// the value already exists (just created or not), so exit
if
(
thisNode
.
Value
==
@new
)
{
break
;
}
var
i
=
0
;
// index of the child of interest
var
inserted
=
false
;
// if we decide to go inside child or not
// traverse children in order
while
(
i
<
thisNode
.
Children
.
Count
)
{
// traverse while the last character is lexicographically no larger than the inserted's last character
if
(
thisNode
.
Children
[
i
].
Value
.
Last
()
<=
@new
[
thisNode
.
Value
.
Length
])
{
// if last characters are equal
if
(
thisNode
.
Children
[
i
].
Value
.
Last
()
==
@new
[
thisNode
.
Value
.
Length
])
{
// dive into child
thisNode
=
thisNode
.
Children
[
i
];
inserted
=
true
;
break
;
}
i
++;
}
else
{
break
;
}
}
// this happens if we have not moved into any child
if
(!
inserted
)
{
// i remembers where we want to insert the element
thisNode
.
Children
.
Insert
(
i
,
new
Node
{
// we insert the prefix only
Value
=
@new
.
Substring
(
0
,
thisNode
.
Value
.
Length
+
1
),
Parent
=
thisNode
});
thisNode
=
thisNode
.
Children
[
i
];
}
}
// update counts
while
(
thisNode
!=
null
)
{
thisNode
.
SubtreeSize
=
thisNode
.
Children
.
Sum
(
ch
=>
ch
.
SubtreeSize
)
+
1
;
thisNode
=
thisNode
.
Parent
;
}
}
}
/// <summary>
/// It should return an array of strings - answers to queries
/// </summary>
/// <param name="w">an array of strings - inputs</param>
/// <param name="queries">an array of integers - queries</param>
/// <returns>an array of strings - answers to queries</returns>
public
string
[]
Solve
(
string
[]
w
,
int
[]
queries
)
{
// construct the trie
PrefixTree
trie
=
new
PrefixTree
();
foreach
(
var
@string
in
w
)
{
// add the input strings
for
(
int
i
=
0
;
i
<
@string
.
Length
;
i
++)
{
// for each string, add itself and all its suffixes (trie will naturally deal with prefixes)
trie
.
Add
(
@string
.
Substring
(
i
,
@string
.
Length
-
i
));
}
}
// run the queries
var
answer
=
new
List
<
string
>();
foreach
(
var
query
in
queries
)
{
answer
.
Add
(
trie
.
Query
(
query
));
}
return
answer
.
ToArray
();
}
}
}
This diff is collapsed.
Click to expand it.
test/hacker-rank/FindStrings.cs
0 → 100644
+
23
−
0
View file @
b318683f
using
Xunit
;
namespace
CodingInterview.Tests.HackerRank
{
public
class
FindStrings
{
[
Fact
]
public
void
TestCases
()
{
// From Hacker Rank
Assert
.
Equal
(
new
string
[]
{
"a"
,
"bc"
,
"INVALID"
},
new
CodingInterview
.
HackerRank
.
FindStrings
().
Solve
(
new
string
[]
{
"abc"
,
"cde"
},
new
int
[]
{
1
,
5
,
20
}));
Assert
.
Equal
(
new
string
[]
{
"aab"
,
"c"
,
"INVALID"
},
new
CodingInterview
.
HackerRank
.
FindStrings
().
Solve
(
new
string
[]
{
"aab"
,
"aac"
},
new
int
[]
{
3
,
8
,
23
}));
// This one is my own
Assert
.
Equal
(
new
string
[]
{
"a"
,
"abc"
,
"cde"
,
"dez"
,
"fh"
,
"z"
,
"INVALID"
},
new
CodingInterview
.
HackerRank
.
FindStrings
().
Solve
(
new
string
[]
{
"abc"
,
"cdez"
,
"cfe"
,
"cfh"
},
new
int
[]
{
1
,
3
,
8
,
15
,
20
,
22
,
25
}));
}
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
sign in
to comment