1. 程式人生 > >[LeetCode] Count The Repetitions 計數重複個數

[LeetCode] 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 ≤ 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;
    }
};

參考資料: