代码随想录算法训练营第九天|28.实现strStr()、459.重复的子字符串

实现strStr()

暴力解法

        考虑创建两个指针i,j,i用于遍历文本串haystack,j用于循环遍历目标串needle,i和j都从0开始,当循环过程中,每个字符都能完全对上,则返回i,表示找到了字符串中第一个匹配项的下标。否则返回-1。具体代码如下。

class Solution {
public:
    int strStr(string haystack, string needle) {
        int maxsize = haystack.size()-needle.size();//获取最大的可能匹配项的下标
        for(int i = 0; i<=maxsize; i++){//遍历i从0到最大的可能匹配项下标
            int lenneedle = needle.size();//创建目标串的一个标志位。当标志位清0,表示完全匹配
            int k = i;
            int j = 0;
            while(lenneedle>0){
                if(haystack[k] == needle[j]){
                    k++;j++;
                    lenneedle--;
                    if(lenneedle == 0)
                        return i;//若标志位清0,返回当前的i,即获取的第一个匹配项的下标
                }
                else
                    break;//若不匹配跳出内循环,i++
            }
        }
        return -1;//否则返回-1
    }
};

算法的时间复杂度O(m*n),m为目标串(模式串)长度,n为文本串长度。空间复杂度为O(1)。

KMP算法

        KMP算法主要用于字符串的匹配,若以暴力法比较的话,最差的情况时间复杂度为O(m*n),原因在于每次遍历都要从当前文本串比对位置的下一位进行对比,KMP算法旨在避免跳回这下一位再重新比对。KMP算法的基本思路是,根据已经比较过的数据,避免暴力解法文本串的回退。

文本串的指针一直前进,若存在与模式串不匹配的情况,模式串回退重新比较,而文本串不动,这里重要的是模式串如何回退,回退多少,这时就需要KMP算法定义的next数组,next数组的本质是目标串(模式串)中“最长的相同前后缀长度”。

求next数组步骤:

可暴力求解,或使用递归的方式来推。(周末看看能不能搞懂)

1.初始化数组,参数i,j;i指向后缀末尾位置,j指向前缀末尾位置(j还代表了包括i之前子串的最长相等前后缀的长度)。j初始化为0,i在循环中更新,i初始化为1。

2.处理前后缀不相同的情况

3.处理前后缀相同情况

4.更新next数组的值

getnext的想法还是不太会,再多看看。

class Solution {
public:
    vector<int> getnext(string s){
        vector<int>next(s.size(),0);
        int prefix_len = 0;//当前共同前后缀的长度.
        int i = 1;
        next[0] = 0;
        while(i < s.size()){
            if(s[prefix_len] == s[i]){
                next[i] = prefix_len + 1;
                i++;
                prefix_len++;
            }
            else{
                if(prefix_len>0){
                    prefix_len = next[prefix_len - 1];
                }
                else{
                    next[i] = 0;
                    i++;
                }

            }
        }
        return next;

    }
    int strStr(string haystack, string needle) {
        vector<int>next = getnext(needle);
        int i = 0; //文本串的指针
        int j = 0; //模式串的指针
        while(i<haystack.size()){
            if(haystack[i] == needle[j]){
                i++;j++;
            }//当两者相同时,i++,j++
            else if(j >0){
                j = next[j - 1];
            }//否则若两者不同,且j>0时,模式串指针回退位置
            else{
                i++;//当j = 0时,表明都不匹配,i后移一位
            }
            if(j == needle.size()){//当j到达模式串尾后一位,表明已经完全匹配
                return i - j;//返回i-j,即为字符串中第一个匹配项的下标。
            }
        }
        return -1;//否则返回-1,未找到
    }
};

算法的时间复杂度为O(n),空间复杂度为O(1)。

重复的子字符串

暴力解法

        暴力解法考虑对所有可能子串与主串进行匹配,当匹配到符合子串则返回true,否则返回false,这里有一个需要注意的点,由于子串需要完全匹配主串,所以子串一定会出现在包含主串首字符的位置,此外,由于是子串,有两个剪枝方法,一是若为重复子串,则最少应在字符主串中出现两次,所以对子串的遍历只需要从0到主串长度/2,此外,主串长度应为子串长度的倍数,即s.size()%substring.size() == 0,若不为0,可直接跳过这段遍历。算法的时间复杂度为O(n^2),空间复杂度为O(1)。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        string substring;
        for(int i = 0; i < s.size()/2; i++){
            substring += s[i];//构建子串,且这个子串必然包含字符串的首字符
            if(s.size()%substring.size()!=0){
                continue;
            }
            int j =0;
            while(j<s.size()) {//针对文本串进行循环,若能完全循环,返回true
                int k = 0;
                while (substring[k] == s[j] and j < s.size()) {
                    k++;
                    j++;
                    if (k == substring.size() and j == s.size())
                        return true;
                    if (k == substring.size())
                        k = 0;
                }
                break;//存在不匹配的,则当前子串不能构成文本串,退出while循环
            }
        }
        return false;//找不到能匹配的子串,返回false
    }
};

移动匹配

KMP算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值