leetcode 466. Count The Repetitions(重复字符串字数)

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

给出字符串S1,S2(注意这里S是大写),问S1里出现了多少次S2。
其中S1是s1(这里小写)重复n1次的字符串,S2是s2(小写)重复n2次的字符串。

思路:
注意大小写,大写的S1,S2表示重复了n1次,n2次的字符串,小写的是原始的没有重复的字符串

参考了方法1方法2

总结上面参考方法的几点:
(1) 不需要把s2重复n2次,只需要看S1(大写的)中出现了多少次s2。假设出现了n次s2, 那么出现S2的次数就是n / n2(因为S2是s2重复了n2次)。

(2) 不需要把S1遍历完,有个简便途径,找到S1中的一个pattern,这个pattern是会重复出现的(因为S1本身就是s1重复出来的)。只需要知道这样的pattern会出现多少次,以及每个pattern中s2会出现多少次即可。
那么这段重复出现pattern的字段中s2出现的次数=pattern次数 ✖️ 每个pattern中s2出现的次数。

(3) pattern出现之前本身s2可能就出现了一次,pattern结束之后可能残留了s2的前一段,然后从pattern结束之后的地方到S1的结尾处可能又出现了s2的后一段,这样在pattern结束之后到S1结尾可能s2还会出现。
因此要考虑pattern出现前的部分和pattern结束后到S1结尾的部分中s2出现的次数。

(4) pattern结束后到S1结尾这段,可能会想那还是要把S1遍历完吧。注意S1本身就是s1重复出来的,到结尾这段只能说明下一个pattern还没有结束,到一部分就结尾了,所以只需要知道到哪一部分结尾了,就可以直接在前面的部分截取。

顺便贴一张参考图
其中nextIndex指s1每重复一次(一个字段),这个字段结束后下一字段开始时要从s2的哪个index开始比较。
Input:
s1=“abacb”, n1=6
s2=“bcaa”, n2=1

                    0 1    2 3 0      1    2 3 0      1    2 3 0  
S1 --------------> abacb | abacb | abacb | abacb | abacb | abacb 

repeatCount ----->    0  |   1   |   1   |   2   |   2   |   3

Increment of 
repeatCount     ->    0  |   1   |   0   |   1   |   0   |   1

nextIndex ------->    2  |   1   |   2   |   1   |   2   |   1
                                     ^
									 |
									 repetitive pattern found here (we've met 2 before)!
									 The pattern repeated 3 times

pattern部分s2出现的次数:
可以看到nextIndex中出现了[1,2]这样的pattern,只需要知道pattern出现了多少次。

pattern结束到S1结尾部分s2出现的次数:
假设上面图中s1重复的字段从下标1~n1,可以看到nextIndex = [1,2]这个pattern是从start=1开始的,第一次pattern结束是下标k=3,那么就可以知道pattern结束后到S1结尾的重复次数是(n1-start) % (k-start), 即结尾处nextIndex=[1]的部分,这个部分是和下标2部分相同的,所以只需要知道下标2处repeatCount的值,也就是这段中s2出现的次数。

当然如果没有出现pattern,那就只能老老实实把S1遍历完了。

    public int getMaxRepetitions(String s1, int n1, String s2, int n2) {
        if(s1 == null || s2 == null) {
            return 0;
        }
        
        //因为每次重复字段都要在它之前找s2有没有出现过,为了保证第1次之前有意义,前面补0
        int[] repeatCount = new int[n1+1];
        int[] nextIndex = new int[n1+1];
        
        int j = 0;
        int cnt = 0;
        
        //从开始到第k次重复的s1中出现s2的次数,如果s2重复出现,则不用再看后面,直接根据重复pattern的特点得出结果
        for(int k = 1; k <= n1; k++) {
            for(int i = 0; i < s1.length(); i++) {
                if(s1.charAt(i) == s2.charAt(j)) {
                    j ++;
                    //s2的index走到结尾,说明s2出现了一次
                    //注意是s2.length(),而不是s2.length()-1,因为超过最后的index直接会被置0
                    //如果是s2.length()-1,那么最后一个index就保存不了了
                    if(j == s2.length()) {
                        j = 0;
                        cnt ++;
                    }
                }
            }
            
            //s1重复第k次的一段对应的s2的index
            nextIndex[k] = j;
            //从开始到重复第k段的s1中,s2出现的次数
            repeatCount[k] = cnt;
            
            for(int start = 0; start < k; start++) {
                if(nextIndex[start] == j) { //k次之前出现过
                    //分3部分,第一部分,start之前重复次数
                    int preRepeat = repeatCount[start];
                    //第二部分,有重复pattern的部分,pattern重复多少次
                    int interval = k - start;  //重复区域的长度
                    //start之前不是pattern,想看pattern重复多少次要把前面去掉
                    int repeat = (n1 - start) / interval;
                    //pattern数 * 每个pattern里重复的次数
                    int patternRepeat = repeat * (repeatCount[k] - repeatCount[start]);
                    //第三部分,pattern后的结尾部分
                    int end = start + (n1 - start) % (k - start);
                    int postRepeat = repeatCount[end] - repeatCount[start];
                    //s2出现n次,那么s2重复n2次后会出现 n/n2次
                    return (preRepeat + patternRepeat + postRepeat) / n2;
                    
                }
            }
        }
        //pattern没有出现,要遍历到n1次结束
        return repeatCount[n1] / n2;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值