Consider a string '1234'
. You need a function which generates all the rotations: '1234', '3412', '4123', '2341'
. I created a simple test suite:
assert rotations('123') == set(['123', '231', '312'])
assert rotations('111') == set(['111'])
assert rotations('197') == set(['197', '971', '719'])
What is the pythonic way to do that? I finished with code below
def rotations(num):
str_num = str(num)
result = set()
for mid in xrange(len(str_num)):
result.add(
str_num[mid:] + str_num[:mid]
)
return result
Approach is very simple, Separate string in two parts first & second, for Left rotation Lfirst = str[0 : d] and Lsecond = str[d :]. For Right rotation Rfirst = str[0 : len(str)-d] and Rsecond = str[len(str)-d : ].
An efficient solution is to concatenate s1 with itself. s2 is a rotation of s1 if and only if it is a substring of the rotated string. In java we can either use string contains or indexOf to check for substring. // a substring of str1 concatenated with str1.
s='1234'
z = {s[x:]+s[:x] for x in range(len(s))}
Your second example suggests that your approach isn't efficient if the number is periodic (e.g. 123123123123). As a solution -- check if a given rotation has already appeared. If so -- the sequence is periodic and you have already discovered all distinguishable rotations, so just return the result:
def rotations(num):
str_num = str(num)
result = set()
for mid in range(len(str_num)):
rot = str_num[mid:] + str_num[:mid]
if rot in result:
return result
else:
result.add(rot)
return result
(I changed your xrange
to range
since I am using Python 3 -- you can of course change back).
Although definitely not the fastest or necessarily most Pythonic answer, you could use collections.deque
for this.
from collections import deque
def rotations(s):
s_dq = deque(s)
result = set()
for _ in xrange(len(s_dq)):
s_dq.rotate(1)
result.add(''.join(s_dq))
return result
And it passes all of your example tests:
def main():
tests = ['123',
'111',
'197']
desired = [set(['123', '231', '312']),
set(['111']),
set(['197', '971', '719'])]
for test, result in zip(tests, desired):
print rotations(test) == result
if __name__ == '__main__':
main()
True
True
True
I would not recommend that you use collections.deque for this particular problem, but it can be applied to more complicated versions of problems like this one. It is a very useful and, in my opinion, underused tool--that's why I included it as an answer to this question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With