代码随想录算法训练营第九天|28. 实现 strStr(),459.重复的子字符串,字符串总结 ,双指针回顾

本文介绍了如何使用KMP算法解决LeetCode中的strStr问题,以及如何检测字符串中是否存在重复的子字符串,包括next数组的构建和重复子串的判断条件。同时提到了暴力解法和移动匹配的思路。
摘要由CSDN通过智能技术生成

28. 实现 strStr()

28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

kmp算法理解上还是有难度,其实真要理解起来还好,但是next数组的代码实现还是不太好想,这个视频可以很好的帮助理解。最浅显易懂的 KMP 算法讲解_哔哩哔哩_bilibili。结合代码随想录节具体步骤和代码实现,会帮助很好的理解KMP算法。代码随想录 

class Solution {
public:
    void getNext(int* next,string& s){
        next[0]=0;
        int j=0;
        for(int i=j+1;i<s.size();i++){
            while(j>0&&s[i]!=s[j]){     //一定要先判断不相等的情况,因为一旦不相等j的值就会改变
                j=next[j-1];
            }
            if(s[i]==s[j]){
                j++;
            }
            next[i]=j;
        }
    }
    int strStr(string haystack, string needle) {
        if(needle.size()==0) return 0;
        int next[needle.size()];
        getNext(next,needle);
        int j=0;
        for(int i=0;i<haystack.size();i++){ 
            while(j>0&&haystack[i]!=needle[j]) j=next[j-1];         //这个地方也是先判断不相等的情况
            if(haystack[i]==needle[j]) j++;
            if(j==needle.size()) return(i-needle.size()+1);
        }

        return -1;
    }
};

 459.重复的子字符串

459. 重复的子字符串 

暴力解法:首先我觉得暴力解法也不太好理解,不过在纸上推一遍还好;

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        if(s.size()==1) return false;
        string tem="";                  
        string str="";
        for(int i=0;i<s.size()/2;i++){
            tem+=s[i];
            while(str.size()<s.size()){
                str+=tem;
                if(str.compare(s)==0&&str.size()==s.size()) return true;
            }
            str="";
        }
        return false;
    }
};

重点就是理解tem和str的作用,tem是重复的子串,如果由多个tem组成的str长度和内容都与s相等,那么s就可以由它的一个子串重复多次构成。

tmp字符串:

  • tmp最初是一个空字符串("")。
  • 在for循环中,逐个字符地将输入字符串s的字符追加到tmp中(tmp += s[i])。
  • tmp的作用是表示一个可能的重复子串。在每次迭代中,它都会从输入字符串s的开头增长一个字符。

str字符串:

  • str最初是一个空字符串("")。
  • 在while循环中,程序将tmp的内容重复追加到str,直到str的长度等于输入字符串s的长度。
  • str的作用是检查由tmp表示的子串的重复是否可以形成原始字符串s
  • 如果在任何时刻str等于s并且它们的长度相同(str.compare(s) == 0 && str.size() == s.size()),则函数返回true,表示s可以通过重复tmp表示的子串构建。

 移动匹配

思路:将s变为s+s,例如abcabc变为abcabcabcabc;掐头去尾(不掐不就等于原来的s了吗)用新的字符串t存起来,如果t中包含s那么s就可以由它的一个子串重复多次构成。可以参考下面的文章,证明了这么做的充分性和必要性。

周期字符串问题(两种方法) | 春水煎茶 - 王超的个人博客 (writings.sh)

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        string t=s+s;
        t.erase(t.begin());
        t.erase(t.end()-1);
        if(t.find(s)!=std::string::npos) return true;    // //如果不等于npos说明能在可能的位置找到s          
        return false;
    }
};

npos是一个常数,表示size_t的最大值(Maximum value for size_t)。许多容器都提供这个东西,用来表示不存在的位置,类型一般是std::container_type::size_type。

KMP法:

参考:代码随想录

首先对于next数组的求法就不多说了。这道题里面如果next数组的最大值(即子串最大相等前后缀不为0),并且字符串长度与最长相等前后缀之差可以被字符串长度整除,那么就说明有重复的子字符串。

class Solution {
public:
    void getNext(int* next,const string& s){
        next[0]=-1;
        int j=-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;
        }
    }

    bool repeatedSubstringPattern(string s) {
        if(s.size()==0) return false;
        int next[s.size()];
        getNext(next,s);
        int len=s.size();               
        if(next[len-1]!=-1&&len%(len-(next[len-1]+1))==0) return true;   //如果next数组的最大值(即子串最大相等前后缀不为0),并且字符串长度与最长相等前后缀之差可以被字符串长度整除,那么就说明有重复的子字符串
        return false;
    }
};

还有一点,关于为什么next[len-1]一定是最大相等前后缀,是因为如果s是由重复子串构成,因为next在读完s的第一个子串之后,next[i]的值会一直增加直到填满next数组。(图示next数组是经过-1操作之后)其实我更倾向于不减的,比较直观好理解一点。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值