代码随想录训练营第九天|LeetCode 28、459、字符串总结

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

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

  • 要在主串/文本串中查找是否出现过模式串

  • KMP:当字符串相匹配的过程中,出现不匹配的情况时,可以利用已匹配过的内容避免从头开始匹配

  • 模式串的前缀表:

  • 作用:当出现不匹配时,模式串应该从哪个位置开始重新匹配

  • 定义:记录下标i及i之前的字符串中,最长的相同前后缀的长度

  • 前缀:不包含最后一个字符的所有以第一个字符开头的连续子串

后缀:不包含第一个字符的所有以最后一个字符作为结尾的连续子串

  • 为什么用前缀表(理解!!!)

  • next数组:前缀表/前缀表统一减1。不涉及原理,只是代码实现有差别

  • 时间复杂度:计算next数组O(m),匹配O(n)。所以KMP是O(m+n)

class Solution {
private:
    //前缀表减1操作
    void getNext(int* next, const string& s) {
        int j = -1;
        next[0] = j;
        for (int i = 1; i < s.size(); ++i) {
            while (j >= 0 && s[i] != s[j + 1]) {
                j = next[j];
            }
            if (s[i] == s[j + 1]) {
                j++;
            }
            next[i] = j; 
        }
    }
public:
    int strStr(string haystack, string needle) {
        //char *strStr(const char *haystack, const char *needle)
        if (needle.size() == 0) return 0;//C库函数strStr()无论haystack是不是空串,只要needle是空串,总是返回haystack的首地址
        if (haystack.size() == 0 && needle.size() > 0) return -1;//当haystack是空串,且needle不是空串时,返回null
        int* next = new int[needle.size()];
        getNext(next, needle);
        int j = -1;
        for (int i = 0; i < haystack.size(); ++i) {
            while (j >= 0 && haystack[i] != needle[j + 1]) {
                j = next[j];
            }
            if (haystack[i] == needle[j + 1]) {
                j++;
            }
            if (j == needle.size() - 1) {
                return (i - needle.size() + 1);
            }
        }
        delete[]next; next = NULL;
        return -1;
    }
};
class Solution {
private:
    //前缀表不减1操作
    void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = j;
        for (int i = 1; i < s.size(); ++i) {
            while (j >= 1 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if (s[i] == s[j]) {
                j++;
            }
            next[i] = j; 
        }
    }
public:
    int strStr(string haystack, string needle) {
        //char *strStr(const char *haystack, const char *needle)
        if (needle.size() == 0) return 0;//C库函数strStr()无论haystack是不是空串,只要needle是空串,总是返回haystack的首地址
        if (haystack.size() == 0 && needle.size() > 0) return -1;//当haystack是空串,且needle不是空串时,返回null
        int* next = new int[needle.size()];
        getNext(next, needle);
        int j = 0;
        for (int i = 0; i < haystack.size(); ++i) {
            while (j >= 1 && haystack[i] != needle[j]) {
                j = next[j - 1];
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == needle.size()) {
                return (i - needle.size() + 1);
            }
        }
        delete[]next; next = NULL;
        return -1;
    }
};

LeetCode 459重复的子字符串

题目链接:459. 重复的子字符串 - 力扣(LeetCode)

  • 移动匹配

  • 将字符串s拼接为t=s+s

  • 将t掐头去尾

  • 在t里寻找是否出现s

//写法一
class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        string t = s + s;
        t.erase(t.begin());t.erase(t.end() - 1);
        return t.find(s) != string::npos;//找不到只能返回npos,这里判断只能和npos对比!!!!
        //std::string的方法find,返回值类型是std::string::size_type,对应的是查找对象在字符串中的位置。如果未找到,该返回值是一个很大的数据,判断时与std::string::npos进行对比
    }
};
//写法二
class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        string t = s + s;
        return t.find(s, 1) != s.size();//没有掐头去尾的操作所以一定能找到,就不用npos对比了,只要找到的位置不是第二个子串的开始位置即可
    }
};
  • 利用KMP

  • 若存在,则重复的子字符串为最长相同前后缀所不包含的子串

class Solution {
private:
    //前缀表不减一
    void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = j;
        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;
        }
    }
public:
    bool repeatedSubstringPattern(string s) {
        if (s.size() == 0) return false;
        int len = s.size();
        int* next = new int[len];
        getNext(next, s);
        if (next[len - 1] == 0) return false;
        else if (len % (len - next[len - 1]) == 0) return true;
        return false;
    }
};

字符串总结

  • 能够自己实现substr、reverse等库函数

  • 双指针法的灵活使用:譬如剑指 Offer 05. 替换空格 - 力扣(LeetCode),大大节省了时间效率

  • 反转字符串时,先整体再局部反转的思想:譬如151. 反转字符串中的单词 - 力扣(LeetCode)、剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)

  • KMP的使用一定要理解并且熟记

你可能感兴趣的:(LeetCode刷题,leetcode,算法)