【Leetcode】466. Count The Repetitions 统计重复个数

1

解法

这个题目首先要转化成S1里总共有多少个s2,然后再整除n2就是M的值。

基于官方解答的思想但是不做法不太一样
中心思想是循环!就跟判断循环小数似的
我们需要找到一个对齐方式,然后判断某个模式是否循环。
比如说,对于官解例子里的s1="abaacdbac"s2="adcbd"
官解找的对齐模式是 遍历到s1的第一个字母时,s2的哪个下标之前的字母已经完成,于是我们有:

abaacdbac abaacdbac abaacdbac abaacdbac abaacdbac
a    d  c  b   d a       d  c  b   d a       d
0         3         1         3         1

不难发现这个是重复的
具体请走官解

我这里找的对齐模式是:s2的最后一个字母对应s1的哪个下标,令s1="abab"s2="baab"我们有:

abab abab abab abab abab
 ba  ab b a ab  ba  ab b
      1      3       1
      5      11      17

可以看出肯定是131313……地循环了。
当我们在真实下标i处对齐了s2的最后一位,那么此时的对应下标为i%l1。如果在前面已经出现过这个下标了,跟判断循环小数一样,循环节就前面的i%l1处开始。
假设从前面在发现循环节之前已经对齐了as2,分别编号为0,...,a-1i%l1所处的编号为b,表示循环节从编号b处开始。

编号为i的对齐处的真实下标为idx[i]

那么循环节的真实长度为L=i-idx[b]
L1=l1*n1,对于剩下的L1-idx[b]个字符,假设L1-idx[b]=xL+y,其中x=(L1-idx[b])//Ly = (L1-idx[b])%L

现在我们来判断S1里有多少个s2,首先,循环节前肯定有b个,然后x个循环节里肯定有a-b个,难点在于剩下的y个字符里有多少个。
不难发现,对于b<=k<aidx[k]-idx[b]表示这个对齐处到循环节开始处的距离,如果idx[k]-idx[b]<=y,那么剩下的y个字符肯定能至少构成k-b+1s2,所以我们要找到满足这个不等式的最大的k
最后s2的数量为:k+1+x*(a-b)

几个需要注意的地方:

  1. 真实下标i不能超过S1的长度L1
  2. 也有可能由于n1太小所以找不到循环节,甚至有可能s1根本没法组成s2,要注意处理
class Solution(object):
    def getMaxRepetitions(self, s1, n1, s2, n2):
        """
        :type s1: str
        :type n1: int
        :type s2: str
        :type n2: int
        :rtype: int
        """
        from bisect import bisect_right
        if n1==0:
            return 0
        l1,l2 = len(s1),len(s2)
        idx = {}
        f = []
        i = j = 0
        L1 = l1*n1
        while i<L1:
            while i<L1 and s1[i%l1]!=s2[j%l2]:
                i += 1
            if i<L1 and (j+1)%l2==0:
                if i%l1 not in idx:
                    idx[i%l1] = len(f)
                    f.append(i)
                else:
                    b = idx[i%l1]
                    a = len(idx)
                    rest = L1-f[b]
                    L = i-f[b]
                    k = bisect_right(f,rest%L+f[b]-1) if rest%L else b
                    # print a,b,rest,L
                    # print idx,f
                    ans = (rest//L)*(a-b)+k
                    return ans//n2
            i += 1
            j += 1
        return len(f)//n2
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值