1. 程式人生 > 其它 >LeetCode-686 重複疊加字串匹配

LeetCode-686 重複疊加字串匹配

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/repeated-string-match

題目描述

給定兩個字串a 和 b,尋找重複疊加字串 a 的最小次數,使得字串 b 成為疊加後的字串 a 的子串,如果不存在則返回 -1。

注意:字串 "abc"重複疊加 0 次是 "",重複疊加 1 次是"abc",重複疊加 2 次是"abcabc"。

示例 1:

輸入:a = "abcd", b = "cdabcdab"
輸出:3
解釋:a 重複疊加三遍後為 "abcdabcdabcd", 此時 b 是其子串。


示例 2:

輸入:a = "a", b = "aa"
輸出:2


示例 3:

輸入:a = "a", b = "a"
輸出:1


示例 4:

輸入:a = "abc", b = "wxyz"
輸出:-1

提示:

1 <= a.length <= 104
1 <= b.length <= 104
a 和 b 由小寫英文字母組成

解題思路

本題可以拆解為兩部分,首先是找到b串第一次出現的位置,然後計算a串重複的次數。

首先是a串重複次數的部分,當確定了b串第一次出現的位置index後,如果a串大小減去index後依然比b串大小大,那麼a串完全可以覆蓋掉b串,此時返回1.如果a串剩餘的部分比b串的大小小,那麼可以將重複n次的a串根據index斬頭去尾然後除以a串的大小,這就是b串內部需要a串重複的次數,然後加上頭與尾超出的部分兩次,就可以求出n。

接下來的問題就轉換為計算重複a串中b串第一次出現位置的問題。可以使用KMP演算法或者RK演算法來求解。

1、KMP演算法。

KMP演算法是利用匹配串也就是b串中重複字首減少匹配次數的一直快速求取字串匹配的方法。具體方法是維護一個next索引,將有共同字首的字元跳轉索引記錄下來,下次就可以直接跳轉到那個位置,減少重複匹配次數。具體可以參考此文

2、RK演算法。

RK演算法是維護一個與匹配串也就是b串大小相同的滑動視窗,如果滑動視窗和a串相同,那麼就可以判斷第一次出現的位置,但是比傳統樸素匹配優秀的地方是,字串會被雜湊轉換成一個長整形的數字,比較長整形的數字的速率是比比較字串快的,而且視窗滑動的時候,僅需要將左部字元雜湊值減去再加上右部字元的雜湊值就可以,不需要重複遍歷中間字元。

程式碼展示

KMP演算法:

class Solution {
public:
    int KMP(string a, string b)
    {
        int m = a.size(), n = b.size();
        vector<int> viNext(n, 0);
        for(int i = 1, j = 0; i < n; i++)
        {
            while(b[i] != b[j] && j > 0)
            {
                j = viNext[j - 1];
            }
            if(b[i] != b[j])
            {
                viNext[i] = 0;
            }
            else
            {
                viNext[i] = j + 1;
                j++;
            }
        }
        for(int i = 0, j = 0; i < m + n; i++)
        {
            while(a[i % m] != b[j] && j > 0)
            {
                j = viNext[j - 1];
            }
            if(a[i % m] != b[j])
            {
                j = 0;
            }
            else
            {
                j++;
            }
            if(j == n)
            {
                return i - n + 1;
            }
        }
        return -1;
    }
    int repeatedStringMatch(string a, string b) {
        int iRet = KMP(a, b);
        if(iRet != -1)
        {
            if(a.size() - iRet >= b.size())
            {
                iRet = 1;
            }
            else
            {
                iRet = 2 + (b.size() - a.size() + iRet - 1 ) / a.size();
            }
        }
        
        return iRet;
    }
};

RK演算法:

class Solution {
public:
    int RK(string a, string b)
    {
        int m = a.size(), n = b.size();
        if(n == 0) return 0;

        long long k1 = 1e9 + 7;
        long long k2 = 1337;
        srand((unsigned)time(NULL));
        long long kMod1 = rand() % k1 + k1;
        long long kMod2 = rand() % k2 + k2;
        long long llb = 0;
        for(auto c: b)
        {
            llb = (llb * kMod2 + c) % kMod1;
        }
        long long lla = 0, llTemp = 1;
        for(int i = 0; i < m + n - 1; i++)
        {
            lla = (lla * kMod2 + a[i % m]) % kMod1;
            if(i < n - 1)
            {
                llTemp = (llTemp * kMod2) % kMod1;
            }
            else
            {
                if(lla == llb)
                {
                    return i - n + 1;
                }
                lla = (lla - a[(i - n + 1) % m] * llTemp) % kMod1;
                lla = (lla + kMod1) % kMod1;
            }
        }

        return -1;
    }
    int repeatedStringMatch(string a, string b) {
        int iRet = RK(a, b);
        if(iRet != -1)
        {
            if(a.size() - iRet >= b.size())
            {
                iRet = 1;
            }
            else
            {
                iRet = 2 + (b.size() - a.size() + iRet - 1 ) / a.size();
            }
        }
        
        return iRet;
    }
};

執行結果