I'm looking for any possible improvements in terms of memory usage and speed, but really any advice is welcome.Problem statement:John has discovered various rocks. Each rock is composed of various  elements, and each element is represented by a lower-case Latin letter  from 'a' to 'z'. An element can be present multiple times in a rock.  An element is called a gem-element if it occurs at least once in each  of the rocks.Given the list of  rocks with their compositions, display the number  of gem-elements that exist in those rocks.Input FormatThe first line consists of an integer, , the number of rocks.  Each of  the next  lines contains a rock's composition. Each composition  consists of lower-case letters of English alphabet.Constraints    Each composition consists of only lower-case Latin letters ('a'-'z').   length of each composition Output FormatPrint the number of gem-elements that are common in these rocks. If  there are none, print 0.My code: Essentially I build N lists of True or False values (since the number of times the same element appears doesn't matter), and at the end just and them two by two using reduce to end up with a final list of True values for the elements that were True in each rock, and then just sum up the True values.import sysfrom functools import reduce# this is just to get test cases inputN = int(input())strings = []for i in range(N):    strings.append(input())# actual implementation starts herelists = [[False] * 26 for i in range(N)]for i in range(N):    for c in strings[i]:        # ord() gives the ASCII value of a character        lists[i][ord(c) - ord('a')] = Truefinal_list = reduce(lambda x, y: [x[i] and y[i] for i in range(26)], lists)print(reduce(lambda x, y: x + y, final_list))
Hackerrank Gemstones Solution
python;performance;algorithm;programming challenge
There are several ways you can save memory, where youre storing more information than you really need. In some cases, way more information than you need.If you only save what you need, youll reduce memory and get a performance benefit to boot!Memory improvement #1These three lines are wasting memory:strings = []for i in range(N):    strings.append(input())You dont actually need to gather up all the rocks into a list  youre only ever looking at one at a time. Although in this case its unlikely to matter, creating a list when you dont need one is a bad antipattern.You can rewrite your code to make the strings list unnecessary, thus saving memory, like so:lists = [[False] * 26 for i in range(N)]for i in range(N):    rock = input()    for c in rock:        # ord() gives the ASCII value of a character        lists[i][ord(c) - ord('a')] = TrueNote that this also only features one for loop of length N, which will likely have a (small) benefit to performance.Memory improvement #2The lists variable (thats not a great name, btw) is a 26 x N-sized matrix. Again, this has the potential to get quite large if you have lots of rocks, or lots of different elements. And youre storing more data than you need to  all were interested in is which elements were present in each rock. A simpler way to store that would be something like:rock_elements = []for _ in range(N):    rock = input()    rock_elements.append(set(rock))final_list = reduce(lambda x, y: [i in x and i in y for i in range(26)], lists)Now the rock_elements list only contains the set of elements in each rock. Were not provisioning lots of unwanted memory for small rocks.Memory improvement #3Were still storing more information than we need. For example, suppose the first rock omits a, and every other rock contains a. Remembering that those later rocks contain a is pointless, because we know that a isnt a gem-element.So an even more efficient solution would be to keep a set of possible gem-elements, and throw elements away whenever we find a rock that doesnt contain them. Like so:import stringgem_elements = set(string.ascii_lowercase)for _ in range(N):    rock = input()    gem_elements = gem_elements & set(rock)print(len(gem_elements))Now our gem_elements set will never be larger than the number of possible elements, and were only storing the data for one rock at a time. Much better!And as a nice side benefit, weve got rid of the two calls to reduce(), which are:Generally not very Pythonic  idiomatic Python tends towards list comprehensions and similar rather than reduce() and map().Moderately expensive  I think at least \$O(N)\$ in the number of rocks?