Skip to content

Commit 4637d0e

Browse files
author
Albert Hu
authored
Merge pull request #18 from alberthu16/day24
Day24: Strings Rearrangement (backtracking)
2 parents f41d339 + a0acf30 commit 4637d0e

File tree

4 files changed

+138
-0
lines changed

4 files changed

+138
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ Challenges by day (4 days missed):
3232
[Day 21](./day21) - (CodeFights) Helping Stephan
3333
[Day 22](./day22) - Permutations of a list
3434
[Day 23](./day23) - Strings Rearrangement
35+
[Day 24](./day24) - Strings Rearrangement (backtracking)
3536

3637

day23/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,18 @@ Any better way to approach this problem? I think so..
4141

4242
https://en.wikipedia.org/wiki/Hamiltonian_path_problem
4343

44+
also check out this minified and slightly worse runtime version LOL (180 characters)
45+
46+
```python
47+
from itertools import permutations as p
48+
49+
def stringsRearrangement(i):
50+
return any(n(x) for x in p(i))
51+
52+
def n(i):
53+
return len(i) <= 1 or (d(i[0], i[1]) and n(i[1:]))
54+
55+
def d(w, a):
56+
return sum(i != j for i, j in zip(w, a)) == 1
57+
```
58+

day24/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Today's challenge is a continuation of [yesterday's](../day23)!
2+
3+
## Ideas
4+
5+
I wanted to take a different approach to this question; instead of doing a
6+
full brute-force exploration on all combinations of the inputArray,
7+
construct a graph and then do DFS from every node to find a possible
8+
Hamiltonian path. The existence of a path would verify whether there is
9+
a desired arrangement of the strings.
10+
11+
I can construct the graph in `O(N<sup>2</sup>)` time where `N` is the number of
12+
elements in the inputArray, and also the number of vertices in my graph. I check
13+
every possible pair of strings to see whether they differ by exactly one place,
14+
and add an edge in the graph between the two if so.
15+
16+
I can then run DFS from each node in `N * O(N) = O(N<sup>2</sup>)` time (this
17+
analysis doesn't seem right to me actually) to complete the algorithm.
18+
19+
Overall, it comes out to a runtime of `O(n<sup>2</sup>)`. The space complexity
20+
scales in proportion to the number of vertices and edges I need to keep track
21+
of in my graph. Vertices increase with every additional element in the inputArray.
22+
Edges increase when there are higher frequencies of words in the inputArray
23+
that are closer to each other in edit distance.
24+
25+
## Code
26+
27+
[Python](./stringsRearrangementBacktracking.py) (unfinished)
28+
29+
## Follow up
30+
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
def differByOne(word, anotherWord):
2+
return sum(c1 != c2 for c1, c2 in zip(word, anotherWord)) == 1
3+
4+
def tuplelizeDuplicates(inputArray):
5+
dups = {elem:0 for elem in inputArray}
6+
outputTuples = []
7+
for elem in inputArray:
8+
if elem in dups:
9+
outputTuples.append((elem, dups[elem]))
10+
dups[elem] += 1
11+
return outputTuples
12+
13+
def createGraph(inputTuples):
14+
g = {elem:set() for elem in inputTuples}
15+
16+
size = len(inputTuples)
17+
18+
for i in xrange(size):
19+
for j in xrange(size):
20+
21+
if i == j:
22+
continue
23+
24+
v1 = inputTuples[i]
25+
v2 = inputTuples[j]
26+
27+
if differByOne(v1[0], v2[0]):
28+
g[v1].add(v2)
29+
g[v2].add(v1)
30+
return g
31+
32+
def derp(graph, startTuple, visited=set()):
33+
# do dfs
34+
for vertexTuple in graph[startTuple]:
35+
print vertexTuple
36+
if vertexTuple not in visited:
37+
visited.add(vertexTuple)
38+
if derp(graph, vertexTuple, visited):
39+
return True
40+
print visited
41+
42+
if len(visited) == len(graph):
43+
return True
44+
return False
45+
46+
def hamiltonianPath(graph):
47+
if len(graph) == 0:
48+
return True
49+
50+
print graph
51+
for node in graph:
52+
if derp(graph, node):
53+
return True
54+
55+
return False
56+
57+
def testDifferByOne():
58+
assert not differByOne("", "")
59+
assert not differByOne("a", "a")
60+
assert not differByOne("aaa", "aaa")
61+
assert not differByOne("abcdeff", "abcedff")
62+
assert differByOne("a", "b")
63+
assert differByOne("abc", "abb")
64+
assert differByOne("abc", "bbc")
65+
assert differByOne("abcdefg", "abcdefz")
66+
67+
def testTuplelizeDuplicates():
68+
assert tuplelizeDuplicates(["qq", "qq", "qq"]) == [("qq", 0), ("qq", 1), ("qq", 2)]
69+
70+
def testCreateGraph():
71+
assert createGraph(tuplelizeDuplicates(["aba", "bbb", "bab"])) == {("aba", 0): set([]), ("bbb", 0): set([("bab", 0)]), ("bab", 0): set([("bbb", 0)])}
72+
assert createGraph(tuplelizeDuplicates(["qq", "qq", "qq"])) == {('qq', 1): set([]), ('qq', 0): set([]), ('qq', 2): set([])}
73+
assert createGraph(tuplelizeDuplicates(["ab", "ad", "ef", "eg"])) == {('ab', 0): set([('ad', 0)]), ('ef', 0): set([('eg', 0)]), ('ad', 0): set([('ab', 0)]), ('eg', 0): set([('ef', 0)])}
74+
75+
def testHamiltonianPath():
76+
assert hamiltonianPath(createGraph([]))
77+
assert not hamiltonianPath(createGraph(["aba", "bbb", "bab"]))
78+
assert hamiltonianPath(createGraph(["ab", "bb", "aac"]))
79+
assert not hamiltonianPath(createGraph(["qq", "qq", "qq"]))
80+
assert hamiltonianPath(createGraph(["aaa", "aba", "aaa", "aba", "aaa"]))
81+
assert not hamiltonianPath(createGraph(["ab", "ad", "ef", "eg"]))
82+
assert hamiltonianPath(createGraph(["abc", "abx", "axx", "abx", "abc"]))
83+
assert hamiltonianPath(createGraph(["f", "g", "a", "h"]))
84+
85+
def main():
86+
testDifferByOne()
87+
testTuplelizeDuplicates()
88+
testCreateGraph()
89+
testHamiltonianPath()
90+
91+
if __name__ == "__main__":
92+
main()

0 commit comments

Comments
 (0)