A very light and fast array based python solution in < 30 lines
Intuition\nFigured on using arrays to store contiguous runs of alternating colors, then compute the beg/end of each contig group, then the length of each group.
# Intuition\nFigured on using arrays to store contiguous runs of alternating colors, then compute the beg/end of each contig group, then the length of each group. From the lengths, figure you can get Lgroup - Lquery + 1 matches for each group. I thought I\'d do an "incermental" update on the arrays as the colors update, to avoid recomputing in their entirity.\n\n# Approach\nTurned out to be a bit trick getting the boundaries right for different query sizes. Stored the arrays double size to hangle wrapping around, just to make things easier. Not entirely clear doing the harder approach of mod N etc on a single size arry would be any faster, esp. in python. Would cut the memory use in half tho. I was planning to do smart incermental update on the groups/breaks/lens as the colors change. Implemented this for blocks, trading one simple line of code for 3 complicated ones and ugly bounds checking. The "smart" update for blocks only sped things up by roughly 3%, so I lost motivation to implement the more complicated smart updates for breaks/lens. In theory, it might be possible to only update breaks / lens, which may be pretty sparse for the big test cases... this might make things significantly faster. \n\n# Complexity\n- Time complexity:\n    O(Q(N+Q))\n- Space complexity:\n    O(N)\n\n# Code\n```\nimport numpy as np\n\nclass Solution:\n    def numberOfAlternatingGroups(self, colors, queries):\n        N = len(colors)\n        colors = np.tile(np.array(colors, dtype=bool), 2)  # wrap around\n        blocks = (colors[1:] != colors[:-1])  # runs of True will be "groups," but ending early by 1\n        result = list()\n        for query in queries:\n            if query[0] == 1:\n                size = query[1]\n                blkcrop = blocks[:N + size - 1]  # crop to appropriate wrap around\n                diff = (blkcrop[1:N + size - 1] != blocks[:N + size - 2])\n                breaks = np.nonzero(diff)[0] + 1  # index of group beg/eng\n                if blkcrop[+0]: breaks = np.concatenate([[0], breaks])\n                if blkcrop[-1]: breaks = np.concatenate([breaks, [N + size - 2]])\n                lens = breaks[1::2] - breaks[0::2] + 1  # lengths of the groups; +1 b/c they end early\n                count = np.sum(np.maximum(0, lens - size + 1))  # size that can be packed into groups\n                result.append(count)\n            else:\n                i, c = query[1:]\n                if colors[i] == c: continue\n                colors[i:None:N] = c\n                j, k = max(1, i), min(i, N - 2)  # for bounds checking\n                blocks[j - 1:i + 1] = colors[j:i + 2] != colors[j - 1:i + 1]\n                blocks[i - 1 + N:k + 1 + N] = colors[i + N:k + 2 + N] != colors[i - 1 + N:k + 1 + N]\n                # blocks = (colors[1:] != colors[:-1])  # the simple update is only slightly slower\n        return result\n\n      \n```
['Array', 'Python3']