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; } };
运行结果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了