LeetCode 466 - Count The Repetitions - Hard ( Python)

Define S = [s,n] as the string S which consists of n connected strings s. For example, ["abc", 3] ="abcabcabc".

On the other hand, we define that string s1 can be obtained from string s2 if we can remove some characters from s2 such that it becomes s1. For example, “abc” can be obtained from “abdbec” based on our definition, but it can not be obtained from “acbbe”.

You are given two non-empty strings s1 and s2 (each at most 100 characters long) and two integers 0 ≤ n1 ≤ 106 and 1 ≤ n2 ≤ 106. Now consider the strings S1 and S2, where S1=[s1,n1] and S2=[s2,n2]. Find the maximum integer M such that [S2,M] can be obtained from S1.

Example:

Input:
s1="acb", n1=4
s2="ab", n2=2

Return:
2思路1:Brute force. 我们在s1中找出全部的单个s2,(遍历s1)然后把这个数量除以n2就可以。但是这样做的话,可能n1很大,会造成超时。因为此时的时间复杂度是O(n1*s1)思路2:A better brute force. 我们不需要全部遍历完s1, 我们只需要在s1中找到s2出现的规律就可以。比如说s1 = ‘abcabcabc‘,s2= ‘a‘。我们会发现每一个s1的block(‘abc‘)a都是出现1次。那么总共会有n1*1次出现s2, n1*1/n2就是最大程度s2会出现的个数了。所以下一步我们需要寻找的是s2在s1中出现的规律。我们用一个pos数组来记录每一个s1 block在开头的位置会遇到的s2的index。如果我们发现在某一个block中,s1开头的遇到的index等于此时指针遍历到s2的index,那么意味着repetition已经出现过了,我们要找的pattern已经找到了。计算出pre_count, pattern_count以及remaining count,得到的结果除以n2就是我们的最终答案。步骤:1 构造counter 和pos两个数组。分别用来记录s1中每一个block开头遇到的s2的count数量以及相当于的在s2中的index位置。数组的大小为len(s2)+1.      2 两个指针一个i先遍历从0到n1-1,另一边j遍历从0到len(s1)-1.如果s1[j] == s2[index]. increment index. 如果index等于len(s2)。那么意味我们已经完全找到了s2.此时count+=1,index重新归零。当j遍历完一遍的时候,也就是我们遍历完一个s1的时候,更新此时的counter[i] pos[i]( counter[i] = count, pos[i] = index).      3 用另一个指针k从0到i遍历,如果我们发现了重复,也就是之前有一个pos[k]等于现在的index. 那么我们计算三个部分,pre_count, pattern_count,remaining_count. pre_count等于counter[k],pattern_count = (counter[i]-counter[k]) *(n1 - k - 1) // (i-k). remain_count = counter[k+(n1-1-k) % (i-k)] - counter[k]. 所以我们得到pre_count, pattern_count, remaing count 之后,三者相加除以n2,得到的就是值。     4 如果没有出现repetition pattern,也就是循环没有进入到上面第3步的时候,我们得到的counter的最终值,counter[n1-1], 除以n2得到的就是答案。时间复杂度 O(len(s1)*len(s2)) 空间复杂度 O(len(s2))用来储存s2的大小。
class Solution:
    def getMaxRepetitions(self, s1: str, n1: int, s2: str, n2: int) -> int:
        if n1 == 0: return 0 
        
        pos = [0] * (len(s2) + 1) 
        counter = [0] * (len(s2) + 1)
        
        index = 0 
        count = 0 
        
        for i in range(n1):
            for j in range(len(s1)):
                if s1[j] == s2[index]:
                    index += 1 
                if index == len(s2):
                    index = 0 
                    count += 1 
            counter[i] = count 
            pos[i] = index 
            
            for k in range(i):
                if pos[k] == index: # means the same k and the same index meet each other again. Because index = pos[i]. 
                    pre_count = counter[k]
                    patten_count = (counter[i] - counter[k]) * ((n1-1-k) //(i-k))
                    remain_count = counter[k+(n1-1-k) % (i-k)] - counter[k]
                    
                    return int((pre_count + patten_count + remain_count) / n2) 
        return int(counter[n1-1] / n2 )