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 ≤ 106and 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
这道题放了好久才写,主要是因为这道题难度确实不小,光是分析研究网上大神们的帖子就搞了好久,就是现在也不能说完全理解了这道题,哎,将就着写吧,有不足的地方欢迎指正。主要参考了网上大神lzl124631x的帖子,还有大神aaaeeeo的帖子。 这道题的Java版本的brute force可以通过OJ,但是C++的就不行了,我们需要使用重复模式来优化我们的方法,我们知道:
如果s2在S1中出现了N次,那么S2肯定在S1中出现了N/n2次,注意这里的大写表示字符串加上重复次数组成的大字符串。
所以我们得出结论,我们只要算出s2出现的次数,然后除以n2,就可以得出S2出现的次数了。
那么问题就是我们表示重复,我们遍历s1字符串n1次,表示每个s1字符串为一段,对于每段,我们有:
1. 出现在该段的s2字符串的累计出现次数
2. 一个nextIndex,其中s2[nextIndex]表示在下一段s1中你所要寻找的s2中的一个字符。(比如说s1=”abc”, s2=”bac”, 由于第一个s1中只能匹配上s2中的b,那么只有在下一段s1中才能继续匹配s2中的a,所以nextIndex=1,即a在s2中的位置为1;同理,比如s1=”abca”, s2=”bac”,第一个s1可以匹配上s2中的ba,那么后面的c只能在下一段s1中匹配上,那么nextIndex=2,即c在s2中的位置为2)
表示重复关键就在于nextIndex,比如对于下面这个例子:
Input: s1="abacb", n1=6 s2="bcaa", n2=1 Return: 3
j ---------------> 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 nextIndex -------> 2 | 1 | 2 | 1 | 2 | 1
nextIndex的范围从0到s2.size()-1,根据鸽巢原理(又称抽屉原理),你一定会找到相同的两个nextIndex在遍历s1段s2.size()+1次之后。在上面的例子中,重复的nextIndex出现在第三段,和第一段一样都为2,那么重复的pattern就找到了,是第二段和第三段中的aabc,而且从第四段开始,每两段就有一个aabc,现在我们的目标就是统计出整个S1中有多少个s2。
由于pattern占用了两段,所以interval为2,我们然后看整个S1中有多少个这样的两段,repeat = (n1 – start) / interval。start表示pattern的起始段数,之前的不是pattern,然后我们算在整个S1中有多少个pattern出现,patternCnt = (repeatCnt[k] – repeatCnt[start]) * repeat,注意这里的repeatCnt[k] – repeatCnt[start]表示一个pattern中有多少个字符串s2,个人感觉一般来说都是1个。然后我们算出剩下的非pattern的字符串里能包含几个s2,remainCnt = repeatCnt[start + (n1 – start) % interval],然后我们把patternCnt + remainCnt之和算出来除以n2就是需要的结果啦。如果pattern未曾出现,那么我们直接用repeatCnt[n1] / n2也能得到正确的结果,参见代码如下:
class Solution { public: int getMaxRepetitions(string s1, int n1, string s2, int n2) { vector<int> repeatCnt(n1 + 1, 0); vector<int> nextIdx(n1 + 1, 0); int j = 0, cnt = 0; for (int k = 1; k <= n1; ++k) { for (int i = 0; i < s1.size(); ++i) { if (s1[i] == s2[j]) { ++j; if (j == s2.size()) { j = 0; ++cnt; } } } repeatCnt[k] = cnt; nextIdx[k] = j; for (int start = 0; start < k; ++start) { if (nextIdx[start] == j) { int interval = k - start; int repeat = (n1 - start) / interval; int patternCnt = (repeatCnt[k] - repeatCnt[start]) * repeat; int remainCnt = repeatCnt[start + (n1 - start) % interval]; return (patternCnt + remainCnt) / n2; } } } return repeatCnt[n1] / n2; } };
参考资料:
https://discuss.leetcode.com/topic/70667/c-0ms-o-str1-length-str2-length/2
https://discuss.leetcode.com/topic/70707/ugly-java-brute-force-solution-but-accepted-1088ms
https://discuss.leetcode.com/topic/72105/c-solution-inspired-by-70664914-with-organized-explanation