Leetcode算法训练日记 | day09

一、实现strStr函数

1.题目

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

示例 1:
输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1

2.解题思路

使用KMP算法,先计算next数组,该数组用于在字符串匹配中跳过已经知道的不匹配前缀,
 再根据next数组遍历模式串和字符串,直到找到完全匹配的字符串,并返回第一个匹配项的下标。

3.实现代码

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Solution 
{
public:
    // getNext函数用于计算KMP算法的next数组,
    // 该数组用于在字符串匹配中跳过已经知道的不匹配前缀
    void getNext(vector<int>&next, const string& s) 
    {
        int j = 0; // 初始化j为0,表示当前没有匹配的前缀和后缀
        next[0] = 0; // 对于模式串的第一个字符,next数组的第一个值设为0
        for (int i = 1; i < s.size(); i++)  // 从模式串的第二个字符开始遍历
        {
            // 当前缀和后缀不匹配时,根据next数组回溯到上一个匹配的位置
            while (j > 0 && s[i] != s[j]) 
            {
                j = next[j - 1];
            }

            // 如果前缀和后缀匹配,j向前移动到下一个位置
            if (s[i] == s[j]) 
            {
                j++;
            }
           
            next[i] = j; // 记录当前位置的前缀和后缀的最长匹配长度
        }
    }

    // strStr函数使用KMP算法在字符串haystack中查找子字符串needle的起始索引
    int strStr(string haystack, string needle) 
    {
        // 如果needle为空字符串,直接返回0,表示找到了匹配(空字符串可以在任何位置开始)
        if (needle.size() == 0) 
        { 
            return 0;
        }
        
        vector<int> next(needle.size());// 分配next数组,用于存储needle的next值
        getNext(next, needle); // 计算needle的next数组
       
        int j = 0; // 初始化j为0,表示当前没有匹配的前缀和后缀

        for (int i = 0; i < haystack.size(); i++) // 遍历haystack中的每个字符
        { 
            // 当haystack中的字符与needle中的前缀和后缀不匹配时,根据next数组回溯
            while (j > 0 && haystack[i] != needle[j]) 
            {
                j = next[j - 1];
            }

            // 如果当前字符匹配,j向前移动到下一个位置
            if (haystack[i] == needle[j]) 
            {
                j++;
            }

            // 如果j等于needle的长度,表示找到了匹配
            if (j == needle.size()) 
            {
                // 返回匹配的起始索引,即i减去needle的长度再加1
                return (i - needle.size() + 1);
            }
        }
        // 如果遍历完haystack后没有找到匹配,返回-1
        return -1;
    }
};

//测试
int main()
{
    Solution s;
    string haystack = "abcsadbutsad";
    string needle = "sad";
    cout <<  "haystack = " << haystack << endl;
    cout << "needle = " << needle << endl;
    cout <<"在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标:"<<s.strStr(haystack,needle) << endl;
    cout <<endl;
    return 0;
}

二、重复的子字符串

1.题目

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

示例 1:
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:
输入: s = "aba"
输出: false

示例 3:
输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)

2.解题思路

使用KMP算法,先计算next数组,检查next数组的最后一个值不为0,则说明字符串有最长相同的前后缀,并且当s的长度可以被(s的长度 - 最长相等前后缀的长度), 即第一个重复长度整除时,说明字符串可以由子串重复多次构成。

3.实现代码
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    // getNext函数用于计算字符串s的next数组,该数组用于KMP算法中的部分匹配表
    void getNext(vector<int>&next, const string& s) 
    {
        int j = 0; // 初始化j为0,表示当前没有匹配的前缀和后缀
        next[0] = 0; // 对于字符串的第一个字符,next数组的第一个值设为0
        for (int i = 1; i < s.size(); i++) // 从字符串的第二个字符开始遍历
        { 
            while (j > 0 && s[i] != s[j]) 
            { 
                j = next[j - 1];// 当前缀和后缀不匹配时,根据next数组回溯
            }
            if (s[i] == s[j]) 
            { 
                j++;// 如果前缀和后缀匹配,j向前移动到下一个位置
            }
            next[i] = j; // 记录当前位置的前缀和后缀的最长匹配长度
        }
    }

    // repeatedSubstringPattern函数用于检查字符串s是否可以由其某个子字符串重复构成
    bool repeatedSubstringPattern(string s) 
    {
        if (s.size() == 0) // 如果字符串为空,直接返回false
        { 
            return false;
        }

        vector<int> next(s.size()); // 创建next数组,用于存储计算结果
        getNext(next, s); // 调用getNext函数计算next数组
        int len = s.size(); // 获取字符串s的长度

        // 检查next数组的最后一个值不为0,则说明字符串有最长相同的前后缀
        // 并且s的长度可以被(s的长度-最长相等前后缀的长度),即第一个重复长度,整除
        if (next[len - 1] != 0 && len % (len - (next[len - 1])) == 0)
        {
            return true; // 如果满足条件,说明s可以由其某个子字符串重复构成
        }
        return false; // 否则,s不能由其某个子字符串重复构成
    }
};

//测试
int main()
{
    Solution p;
    string s = "abab";
    cout << "s = " <<s<< endl;
    if (p.repeatedSubstringPattern(s))
    {
        cout <<s<<"可以由它的一个子串重复多次构成。" << endl;
    }
    else
    {
        cout << s << "不可以由它的一个子串重复多次构成。" << endl;
    }
    cout <<endl;
    return 0;
}

ps:以上皆是本人在探索算法世界旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值