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; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/466
參考資料:
https://leetcode.com/problems/count-the-repetitions/