686. 重复叠加字符串匹配

本文介绍了一种算法,用于解决字符串a重复叠加后使b成为其子串的问题。给出了两种解题思路,一种是通过判断a中是否存在b的首个字符来缩小搜索范围,另一种则是确定复制次数的上下界。

686. 重复叠加字符串匹配

给定两个字符串 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

自己的解题思路

首先,如果a中缺少b中的元素,那么无论a怎么重复b必定不能是a的子串。

那么如果a重复以后b是a的子串,那么第一个b的第一个元素就应该开始能够匹配的到

也就是 abcd中第一个b的元素c开始 cdabcdab刚好匹配到,从而计算出答案,代码如下

class Solution {
public:

    int Isok(int index, string& a, string& b) {  // 函数的作用是判断当前index是否满足条件
        int ans = 1;
        index--;
        for(int i=0;i<b.size();i++) {
            // int index_a = (index+1);
            index++;
            if(index>=a.size()) {
                ans++;
                index%=a.size();
            }
            if(a[index]!=b[i]) {
                return -1;
            }
        }
        return ans;
    }


    int repeatedStringMatch(string a, string b) {
        /* 
            分析,也就是两个字符串 a b 寻找的是,b成为a重叠后的子串。
            首次子串的概念就是,连续的,一致的,并不同于子序列
            首先,重叠以后变成子串必须满足以下的条件
            1. a中必须是b中开头的元素 abcd通过调整顺序就成了 cd开头的
         */

         unordered_map<char,vector<int>> mp;  // vector是为了防止有相同的字符。
         for(int i=0;i<a.size();i++) {
             mp[a[i]].push_back(i);  // 记录的是字符出现的位置,
         }
        
         int ans= -1;
         if(mp.find(b[0])==mp.end()) {
             return ans;
         }

         for(auto index : mp[b[0]]) {
             int tmp = Isok(index,a,b);
             if(tmp>0) {
                 if(ans==-1) {
                     ans=tmp;
                     return ans;
                 } else {
                     ans=min(ans,tmp);
                 }
             }
            //  cout<<ans<<endl;
         }
        

        return ans;

    }
};

参考别人别人解题思路

首先,可以分析复制次数的「下界」和「上界」为何值:

对于「下界」的分析是容易的:至少将 a 复制长度大于等于 b 的长度,才有可能匹配。

在明确了「下界」后,再分析再经过多少次复制,能够明确得到答案,能够得到明确答案的最小复制次数即是上界。

由于主串是由 a 复制多次而来,并且是从主串中找到子串 b,因此可以明确子串的起始位置,不会超过 a 的长度。

由此,我们可知复制次数「上界」最多为「下界 + 11」。

令 a 的长度为 nn,b 的长度为 mm,下界次数为 c1,上界次数为 c2 = c1 + 1。

因此我们可以对 a 复制 c2 次,得到主串后匹配 b,如果匹配成功后的结束位置不超过了 n * c14,说明复制 c1 即可,返回 c1,超过则返回 c2;匹配不成功则返回 -1。

class Solution {
public:
    int repeatedStringMatch(string a, string b) {
        int m = a.size(), n = b.size();
        //b如果有a没有的字符串,则返回-1
        vector<int> alpha(26);
        vector<int> start;//b[0]在a中的所有位置
        for(int i = 0;i < m;i++)
        {
            alpha[a[i]-'a']++;
            if(a[i] == b[0]){
                start.push_back(i);
                // printf("i:%d\n", i);
            }
        }
        for(auto &c: b)
            if(!alpha[c-'a']) return -1;

        //a复制后长度必须不比b短才行
        //思路:字符串哈希,记录a中b开头字母的位置
        //a最大复制长度为b的两倍
        //为了节省空间采用循环
        int res = (n+m-1)/m;
        int len_a = (n+m-1)/m * m;
        bool flag = false;
        int cnt = 2;
        while(cnt--)
        {
            for(auto &pos: start)
            {

                for(int i = pos; i + n - 1 < len_a;i += m){
                    flag = true;
                    for(int j = i;j <= n+i-1;j++)
                    {
                        // printf("i:%d,j:%d,a:%c,b:%c\n",i,j,a[j%m],b[j-i]);
                        if(a[j%m] != b[j-i]) {
                            flag = false;
                            break;
                        }
                    }
                    if(flag) break;
                }
                if(flag) break;
            }
            if(flag) return res;
            res += 1;
            len_a += m;
        }
        return -1;
    }
};

link

在C语言中实现重复叠加字符串匹配问题,可以采用与 C++ 或 Java 中类似的思路:通过不断叠加原始字符串 `A` 来构造一个新的字符串 `S`,并在每次叠加后检查是否包含目标子串 `B`。为了避免无限循环,需要设定一个合理的上限条件来终止循环。 ### 实现思路 1. 创建一个足够大的缓冲区用于存储叠加后的字符串。 2. 使用 `strcat()` 函数将字符串 `A` 不断追加到缓冲区中。 3. 每次追加后使用 `strstr()` 函数判断 `B` 是否为当前缓冲区字符串的子串。 4. 如果找到匹配,则返回当前叠加的次数;如果超出限制仍未找到,则返回 `-1`。 ### 代码示例 ```c #include <stdio.h> #include <string.h> #include <stdlib.h> int repeatedStringMatch(char* A, char* B) { int len_A = strlen(A); int len_B = strlen(B); // 最多叠加次数不会超过 len_B + len_A 次以避免冗余操作 int max_repeats = len_B + len_A; char* buffer = (char*)malloc((len_A * max_repeats + 1) * sizeof(char)); if (!buffer) { fprintf(stderr, "Memory allocation failed\n"); return -1; } strcpy(buffer, A); for (int i = 1; i <= max_repeats; ++i) { if (strstr(buffer, B) != NULL) { free(buffer); return i; } strcat(buffer, A); } free(buffer); return -1; } // 测试函数 int main() { char A[] = "abcd"; char B[] = "cdabcdab"; int result = repeatedStringMatch(A, B); printf("Result: %d\n", result); // 预期输出: 3 return 0; } ``` ### 注意事项 - 在动态分配内存时,必须确保有足够的空间来容纳叠加后的字符串,同时避免越界。 - 使用 `strstr()` 可以高效地查找子串,但需要注意其时间复杂度与字符串长度相关。 - 若叠加后的字符串长度超过一定阈值(如 `10000`),则应提前终止循环以防止性能下降[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值