Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic way to generate string rotations

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
like image 713
kharandziuk Avatar asked Nov 04 '15 12:11

kharandziuk


People also ask

How do you rotate a string in Python?

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 : ].

How do you check whether one string is a rotation of another in Java?

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.


3 Answers

s='1234'
z = {s[x:]+s[:x] for x in range(len(s))}
like image 184
Saiful Azad Avatar answered Sep 29 '22 00:09

Saiful Azad


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).

like image 30
John Coleman Avatar answered Sep 29 '22 00:09

John Coleman


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.

like image 27
pzp Avatar answered Sep 29 '22 02:09

pzp