Skip to content

Commit 4faa3fb

Browse files
committed
feat: add ashton-and-string
1 parent a0e178b commit 4faa3fb

9 files changed

+157
-4
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313

1414
- [`matrix-rotation-algo`](src/matrix_rotation_algo.py): [Matrix Layer Rotation](https://www.hackerrank.com/challenges/matrix-rotation-algo/)
1515

16-
### Advanced difficulty
17-
18-
- [`quadrant-queries-v1`](src/quadrant_queries_v1.py), [`v2`](src/quadrant_queries_v2.py): [Quadrant Queries](https://www.hackerrank.com/challenges/quadrant-queries/)
19-
2016
### Expert difficulty
2117

2218
- [`morgan-and-a-string`](src/morgan_and_a_string.py): [Morgan and a String](https://www.hackerrank.com/challenges/morgan-and-a-string/)
19+
20+
### Advanced difficulty
21+
22+
- [`quadrant-queries-v1`](src/quadrant_queries_v1.py), [`v2`](src/quadrant_queries_v2.py): [Quadrant Queries](https://www.hackerrank.com/challenges/quadrant-queries/)
23+
- [`ashton-and-string`](src/ashton_and_string.py): [Ashton and String](https://www.hackerrank.com/challenges/ashton-and-string/)

src/ashton_and_string.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import itertools
6+
from typing import IO
7+
8+
9+
def ashtonString(s: str, k: int) -> str:
10+
N = len(s)
11+
12+
# suffixRank is table hold the rank of each string
13+
suffixRank = [0] * N
14+
15+
# Example "abaab"
16+
# Suffix Array for this (2, 3, 0, 4, 1)
17+
# Create a tuple to store rank for each suffix
18+
19+
# struct myTuple {
20+
# int originalIndex; // stores original index of suffix
21+
# int firstHalf; // store rank for first half of suffix
22+
# int secondHalf; // store rank for second half of suffix
23+
# };
24+
L = [[0] * 3 for _ in range(N)]
25+
26+
# Initialize suffix ranking on the basis of only single character
27+
# for single character ranks will be 'a' = 0, 'b' = 1, 'c' = 2 ... 'z' = 25
28+
for j in range(N):
29+
suffixRank[j] = ord(s[j]) - ord("a")
30+
31+
# Iterate log(n) times i.e. till when all the suffixes are sorted
32+
# 'stp' keeps the track of number of iteration
33+
# 'cnt' store length of suffix which is going to be compared
34+
#
35+
# On each iteration we initialize tuple for each suffix array
36+
# with values computed from previous iteration
37+
38+
cnt = 1
39+
stp = 1
40+
while cnt < N:
41+
42+
for i in range(N):
43+
L[i][0] = i
44+
L[i][1] = suffixRank[i]
45+
L[i][2] = suffixRank[i + cnt] if (i + cnt) < N else -1
46+
47+
# On the basis of tuples obtained sort the tuple array
48+
# L.sort(key = cmp_to_key(lambda a, b: a[2] - b[2] if a[1] == b[1] else a[1] - b[1]))
49+
L.sort(key=lambda a: a[1] * 2**24 + a[2]) # faster than using `cmp_to_key`
50+
51+
# Initialize rank for rank 0 suffix after sorting to its original index
52+
# in suffixRank array
53+
suffixRank[L[0][0]] = 0
54+
55+
currRank = 0
56+
for i in range(1, N):
57+
# compare ith ranked suffix (after sorting) to (i - 1)th ranked suffix
58+
# if they are equal till now assign same rank to ith as that of (i - 1)th
59+
# else rank for ith will be currRank (i.e. rank of (i - 1)th) plus 1,
60+
# i.e (currRank + 1)
61+
if L[i - 1][1] != L[i][1] or L[i - 1][2] != L[i][2]:
62+
currRank += 1
63+
64+
suffixRank[L[i][0]] = currRank
65+
66+
cnt *= 2
67+
stp += 1
68+
69+
# loop all ordered substrings
70+
seen = set()
71+
length = 0
72+
for i in range(N):
73+
index = L[i][0]
74+
for c in itertools.accumulate(itertools.islice(s, index, N)):
75+
# more memory efficient to store hash of strings seen, instead of strings
76+
hash_c = hash(c)
77+
if hash_c in seen: # check if unique
78+
continue
79+
seen.add(hash_c)
80+
length += len(c)
81+
if length >= k:
82+
return c[k - length - 1]
83+
84+
raise NotImplementedError("should not happen")
85+
86+
87+
def main(fptr: IO) -> None:
88+
t = int(input().strip())
89+
for _ in range(t):
90+
s = input()
91+
k = int(input().strip())
92+
res = ashtonString(s, k)
93+
fptr.write(str(res) + "\n")
94+
95+
96+
if __name__ == "__main__":
97+
if "OUTPUT_PATH" in os.environ:
98+
fptr = open(os.environ["OUTPUT_PATH"], "wt")
99+
else:
100+
fptr = sys.stdout
101+
102+
main(fptr)
103+
104+
fptr.close()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1
2+
dbac
3+
3
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c

src/tests/data/ashton-and-string-08.input

Lines changed: 9 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
m
2+
c
3+
f
4+
v

src/tests/data/ashton-and-string-15.input

Lines changed: 11 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
g
2+
y
3+
c
4+
z
5+
w
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from ..ashton_and_string import main
2+
from . import loop
3+
import sys
4+
5+
6+
def test_answer():
7+
loop.loop_inputs("ashton-and-string-*", main)
8+
9+
10+
def setup_method(self):
11+
self.orig_stdin = sys.stdin
12+
13+
14+
def teardown_method(self):
15+
sys.stdin = self.orig_stdin

0 commit comments

Comments
 (0)