代码随想录训练营day09打卡

找出字符串中第一个匹配的下标

28. 找出字符串中第一个匹配项的下标

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1 。

如果使用暴力枚举,即用i遍历haystack数组,每次从i的位置开始比较,用j指针needle字符串,如果匹配失败就将j回退到neddle字符串开头,并且将i++,这种做法的时间复杂度为O(m*n),其中m是haystack的长度,n是needle的长度。

本题我们使用的是KMP算法实现模式串匹配判断。通过寻找needle和的最长相等前缀,每次匹配失败时,不直接将j指针回退到开头,而是通过前缀表不断递归回退到相等前缀的地方,判断当前字符是否相等,即先判断当前不相等位置的前面串是否存在与后缀相等的前缀,如果存在,则跳转到该串位置,再将haystack[i]与后一个位置neddle[j+1]进行比较,如果不相等,则继续跳转到与neddle[j+1]前面串后缀相等的前缀地方继续进行匹配

while (j>=0 && needle[j+1] != haystack[i]) 
    {
        j = next[j];
    }

如果相等,从当前位置继续遍历即可。而这个需要回退的地方我们使用前缀表记录,即有多大最长长度的相等前后缀(前缀是指不包含最后一个字符且包含第一个字符的连续字符串,后缀是指不包含第一个字符且包含最后一个字符的连续字符串)。如果最后我们的j指针指向了needle数组最后的位置,即每个字符都匹配上了,说明haystack中存在模式串needle。

for (int i = 0; i < haystack.size(); i++)
{
    while (j>=0 && needle[j+1] != haystack[i]) 
    {
        j = next[j];
    }
    if (needle[j + 1] == haystack[i])
    {
        j++;
    }
    if (j == needle.size() - 1)
    {
        return i - j ;
    }
}

那么问题的关键就是如何求得前缀表,这里我们使用的前缀表next[j]是保存的需要跳转的j的位置,而需要比较的是跳转位置后面的位置,因此我们将j的初值和next[0]设置为-1,即当跳到j=-1时,我们比较j+1下标的元素也就是needle[0]。next[j]的后续元素实现方法和模式串匹配类似,我们从下标i=1开始,依次比较前后缀,如果相等,就将j++,然后将j赋值给next[i],说明这一段存在长度为j+1的最长相等前后缀,然后i后移,从之前的最长相等前后缀进行比较,如果当前元素(也就是前面子串的后缀的后一个位置)不同,则回退到前缀,比较前缀的后一个位置,如果依然不同,则将该前缀看成后缀,寻找该前缀的相等前缀(因此这个过程是递归的),最后j指针回退到-1或者能匹配上的第一个前缀末尾。如果匹配上,将j++,此时再将j的位置赋给next[i]。

       int j=-1;
       vector<int> next(needle.size());// abababab
       next[0] = -1;
       for (int i = 1; i < needle.size(); i++)
       {
           while (j >= 0 && needle[j + 1] != needle[i])
           {
               j = next[j];
           }
           if (needle[j + 1] == needle[i])
           {
               j++;
           }
           next[i] = j;
       }

完整代码:

class Solution {
public:
    int strStr(string haystack, string needle) {//needle是子串
        int j=-1;
        vector<int> next(needle.size());// abababab
        next[0] = -1;
        for (int i = 1; i < needle.size(); i++)
        {
            while (j >= 0 && needle[j + 1] != needle[i])
            {
                j = next[j];
            }
            if (needle[j + 1] == needle[i])
            {
                j++;
            }
            next[i] = j;
        }
        for(int i=0;i<next.size();i++)
        cout << next[i];
        j = -1;
        for (int i = 0; i < haystack.size(); i++)
        {
            while (j>=0 && needle[j+1] != haystack[i]) 
            {
                j = next[j];
            }
            if (needle[j + 1] == haystack[i])
            {
                j++;
            }
            if (j == needle.size() - 1)
            {
                return i - j ;
            }
        }
        return -1;
    }
};

重复的子字符串

459. 重复的子字符串

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

KMP算法:通过求得s字符串的next数组,s字符串的长度-s字符串的最大相等前后缀(即next[s.size()-1]的值)所剩下的子串就是要求得的重复子串。如果该子串长度能被s字符串长度整除,说明能由它的一个子串重复多次构成。

假设s="ababababababab"

那么a="ababababababxx"

那么b="xxabababababab"

证明过程:a是相等前缀,b是相等后缀,x表示该位置为空。a[0],a[1]=b[0],b[1]=a[2],a[3]=b[2],b[3]=...=a[s.size-2],a[s.size-3]

代码如下:注意结尾判断条件,当最长相等前后缀长度为0时也应该判为false。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        int j = -1;
        vector<int> next(s.size());// abababab
        next[0] = -1;
        for (int i = 1; i < s.size(); i++)
        {
            while (j >= 0 && s[j + 1] != s[i])
            {
                j = next[j];
            }
            if (s[j + 1] == s[i])
            {
                j++;
            }
            next[i] = j;
        }
        for (int i = 0; i < next.size(); i++)
            cout << next[i];
        return next[s.size() - 1] != -1 && s.size() % (s.size() - next[s.size() - 1] - 1) == 0;
    }
};

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值