代码随想录打卡—day9—【字符串】— KMP

1 KMP知识点复习+套路总结

1.1 前缀:

对一个字符串,包含首字母,不包含尾字母的所有子串。

e.g. aabaaf的前缀:a,aa,aab,aaba,aabaa

1.2 后缀:

对一个字符串,包含尾字母,不包含首字母的所有子串。

e.g. aabaaf的后缀:f,af,aaf,baaf,abaaf

1.3 最长相等前后缀:

对于一个串,前缀和后缀一样的最长的子串

e.g.

串         最长相等前后缀长度【前缀表 = next数组】

a          0

aa        1

aab        0

aaba        1

aabaa        2

aabaaf        0     


1.4 如何利用前缀表(next数组)来匹配?

next数组的含义:

(1)记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。

(2)当主串的第i个元素和模式串的第j个元素失配时,主串的第i个应该和next[ j-1 ] 匹配。

      下标 012345678

文本串:aabaabaaf

      下标 012345

模式串:aabaaf

              010120

所以当文本串第5元素b和模式串的第5元素f不匹配了,模式串就回退到next[5 - 1]  = 2,即模式串的下标为2 的b继续与文本串的下标为5的b继续匹配。


1.5 构造next数组的套路代码

next数组的几种写法:
【1】 = 正常的最长相等前后缀的 (用这个)

【2】正常的最长相等前后缀的全部后移1位,第0号元素设为-1

【3】= 正常的最长相等前后缀的每一位减1


步骤:

设两个指针,i 表示后缀的末尾,j 表示前缀的末尾
【1】初始化

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

【3】处理前后缀相同的情况

【4】更新next[]

代码模板:

        vector<int> next(needle.size());
        int j = 0; //初始化   j表示前缀的最后一位 i表示后缀的最后一位
        next[0] = 0;
        for(int i = 1; i < needle.size();i++)
        {
            while(j > 0 && needle[i] != needle[j])j = next[j - 1];  //前后缀不相同回退
            if(needle[i] == needle[j])j++;  //前后缀相同
            next[i] = j; //求next[]
        }

2 KMP-题1 

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

KMP标准题,复习了carl的视频和讲义才写的。

class Solution {
public:
    int strStr(string haystack, string needle) 
    {
        // 字符串匹配 KMP 但忘差不多了,决定看完视频再回来写!
        // [1]获得模式串needle的next[]
        vector<int> next(needle.size());
        int j = 0; //初始化   j表示前缀的最后一位 i表示后缀的最后一位
        next[0] = 0;
        for(int i = 1; i < needle.size();i++)
        {
            while(j > 0 && needle[i] != needle[j])j = next[j - 1];  //前后缀不相同回退
            if(needle[i] == needle[j])j++;  //前后缀相同
            next[i] = j; //求next[]
        }
        j = 0;  //j遍历模式串
        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;
    }
};

3 KMP-题2

​​​​​​459. 重复的子字符串

KMP变形题目。

【我的尝试】第一时间想法,思路:发现true的好像next[]都有点规律! 0*n(n*k递增序列)。失败了

class Solution {
public:
    bool repeatedSubstringPattern(string s) 
    {
        if(s.size() % 2 != 0)return 0;  //奇数的肯定不行

        vector<int> next(s.size());
        
        int j = 0;
        next[0] = 0;
        cout << next[0] << ' ';


        for(int i = 1; i < s.size();i++)
        {
            while(j != 0 && s[i] != s[j])j = next[j-1];
            if(s[i] == s[j])j++;
            next[i] = j;
            cout << next[i] << ' ';
        }

        // 判断: 前面是k个0 + k倍的递增序列

        int cnt = 0;
        int first = 0;
        int k = 1;
        for(int i = 0; i < s.size();i++)
        {
            while(first == 0 && next[i] == 0)
            {
                i++;
                cnt++;
            }
            first = 1;
            if(i >= s.size() || first && next[i] == 0)return 0;

            if(next[i] != k)return 0;
            k++;
        }
        if(next[s.size()-1] % cnt != 0)return 0;
        return 1;
    }
};

但是在第79个点发现错了,

"abaababaab"的next是0 0 1 1 2 3 2 3 4 5

不符合我猜的规律!!

【方法2】暴力。左边界是固定的,所以遍历右边界即可。O(n^2)AC:

class Solution {
public:
    bool repeatedSubstringPattern(string s) 
    {
        int l = 0;
        for(int i = 0; i < s.size() / 2;i++)  //i表示右边界
        {
            int len = i - l + 1;
            if(s.size() % len != 0)continue;
            string tmp = s.substr(l,len);
            string str = tmp;
            for(int k = 1; k < s.size()/len; k++)tmp += str;
            if(tmp == s)return 1;
        }
        return 0;
    }
};

正确解法:todo

字符串章的总结

carl-字符串总结

双指针问题的总结

carl-双指针总结

总结

这次就复习kmp画的时间比较长!3h。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值