笔记1 第17课 字符串处理 ——Atoi,Rabin-Karp实现strstr,最长回文子串,正则表达式匹配,KMP匹配—— 极客时间算法

之前收藏了极客时间的算法训练营3期 共21课,计划每一课写博客来记录学习,主要形式为

方法类型1

题1

题解

题2

题解

方法类型2

题1

题解

……

题目大体来自leetcode 和 acwing

主要记录和理解代码,所以基本完全搬运了视频题解代码,

个人学习感受体现在大致思路的总结和注释上。


第一题

8. 字符串转换整数 (atoi)

最终还是选用了扩展数据的范围,用long long 来判断是否超界

边界问题超好玩的啦。

class Solution {
public:
    int myAtoi(string s) {
        int index = 0;
        while (index < s.length() && s[index] == ' ') index++;
        int sign = 1;
        bool alreadySign = false;
        while (index < s.length() && s[index] == '+' || s[index] == '-') {
            if (alreadySign) return 0;
            sign = s[index] == '+' ? 1 : -1;
            index++;
            alreadySign = true;
        }
        long long val = 0;
        while (index < s.length() && s[index] >= '0' && s[index] <= '9') {
            if (val * 10 + s[index] - '0' > 2147483647) {
                if (sign == 1) return 2147483647;
                else return -2147483648;
            }
            val = val * 10 + s[index] - '0';
            index++;
        }
        return sign * val;
    }
};

第二题

​​​​​​28. 实现 strStr()

Rabin-Karp

先算哈希值,再比对,

时间复杂度为O(m+n)不过思路可扩展。

class Solution {
public:
    int strStr(string haystack, string needle) {
        int b = 131, p = 1e9 + 7;
        int n = haystack.length();
        int m = needle.length();
        vector h(n + 1, 0);
        // b进制进位, 取模不影响
        for (int i = 1; i <= n; i++) {
            h[i] = (h[i - 1] * b + haystack[i - 1] - 'a' + 1) % p;
        }
        long long Hneedle = 0;
        long long powBM = 1;
        for (char ch : needle) {
            Hneedle = (Hneedle * b + ch - 'a' + 1) % p;
            powBM = powBM * b % p;
        }
        for (int l = 1; l <= n - m + 1; l++) {
            int r = l + m - 1;
            // 相当于做54321 - 54000 = 321
            if (((h[r] - h[l - 1] * powBM) % p + p) % p == Hneedle
             && haystack.substr(l - 1, m) == needle)
                return l - 1;
        }
        return -1;
    }
};

第三题

125. 验证回文串

传string这些容器的时候还是写上引用最好,传值和传址差的太多了,差点超时。

class Solution {
public:
    bool isPalindrome(string s) {
        int l = getNext(s, 0);
        int r = getPre(s, s.length() - 1);
        while (l < r) {
            if (!equalsIgnoreCase(s[l], s[r])) return false;
            l = getNext(s, l + 1);
            r = getPre(s, r - 1);
        }
        return true;
    }
private:
    bool equalsIgnoreCase(char a, char b) {
        if (a >= 'A' && a <= 'Z') a = a - 'A' + 'a';
        if (b >= 'A' && b <= 'Z') b = b - 'A' + 'a';
        return a == b;
    }
    bool isDigitOrLetter (char ch) {
        return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9';
    }
    int getNext(string& s, int i) {
        while(i < s.length() && !isDigitOrLetter(s[i])) i++;
        return i;
    }
    int getPre(string& s, int i) {
        while (i >= 0 && !isDigitOrLetter(s[i])) i--;
        return i;
    }
};

第四题

​​​​​​680. 验证回文字符串 Ⅱ

可以删除一个字母,可以用递归的方式,遇到错误选择忽略一次,从两种下一个位置再递归判断。

class Solution {
public:
    bool validPalindrome(string s) {
        return check(s, 0, s.length() - 1, 1);
    }
private:
    bool check (string& s, int l, int r, int times) {
        if (times < 0) return false;
        while (l < r) {
            if (s[l] != s[r]) return check(s, l + 1, r, times - 1) || check(s, l, r - 1, times - 1);
            l++;
            r--;
        }
        return true;
    }
};

第五题

5. 最长回文子串

将哈希值正反算一遍,如果相同说明是回文。

如是,可以二分答案。

反算哈希时关注对应关系。

class Solution {
public:
    string longestPalindrome(string s) {
        this->s = s;
        n = s.length();
        preH = vector(n + 1, 0);
        sufH = vector(n + 2, 0);
        powB = vector(n + 1, 0);
        powB[0] = 1;
        int ansPos = 0;
        int ansLen = 0;
        for (int i = 1; i <= n; i++) {
            powB[i] = powB[i - 1] * b % p;
            preH[i] = (preH[i - 1] * b + s[i - 1] - 'a' + 1) % p;
        }
        for (int i = n; i >= 1; i--)
            sufH[i] = (sufH[i + 1] * b + s[i - 1] - 'a' + 1) % p;
        //奇回文串
        for (int centre = 0;  centre < n; centre++) {
            int r = min(centre, n - centre - 1);
            int l = 0;
            while (l < r) {
                int mid = (l + r + 1) / 2;
                if (equals(centre - mid, centre + mid, centre - mid, centre + mid))
                    l = mid;
                else 
                    r = mid - 1;
            }
            
            if (r * 2 + 1 > ansLen) {
                ansPos = centre - r;
                ansLen = r * 2 + 1;
            }
        }
        //偶
        for (int centre = 0;  centre < n; centre++) {
            int r = min(centre - 1, n - 1 - centre);
            int l = -1;
            while (l < r) {
                int mid = (l + r + 1) / 2;
                if (equals(centre - 1 - mid, centre + mid, centre - 1 - mid, centre + mid))
                    l = mid;
                else 
                    r = mid - 1;
            }
            
            if (r * 2 + 2 > ansLen) {
                ansPos = centre - 1 - r;
                ansLen = r * 2 + 2;
            }
        }
        return s.substr(ansPos, ansLen);
    }
private:  
    string s;
    int n;
    const int b = 131, p = 1e9 + 7;
    vector preH;
    vector sufH;
    vector powB;
    bool equals(int preL, int preR, int sufL, int sufR) {
        return (((preH[preR + 1] - preH[preL] * powB[preR - preL + 1]) % p  + p) % p) 
            == (((sufH[sufL + 1] - sufH[sufR + 2] * powB[sufR - sufL + 1]) % p + p) % p);
    }
};

第六题

 10. 正则表达式匹配

着重理解    a*   这两个字母是一个整体,它可以匹配任意数量的a,包括0,所以起点要多注意。

class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.length(), n = p.length();
        s = " " + s;
        p = " " + p;
        vector> f(m + 1, vector(n + 1, false));
        f[0][0] = true;
        //初值中,a*a*b*c*这种可以配空值,所以要加上
        for (int i = 2; i <= n; i += 2) {
            if (p[i] == '*') f[0][i] = true;
            else break;
        }
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= n; j++) {
                if (p[j] >= 'a' && p[j] <= 'z') {
                    f[i][j] = f[i - 1][j - 1] && p[j] == s[i];
                }
                else if (p[j] == '.') {
                    f[i][j] = f[i - 1][j - 1];
                }
                else if (p[j] == '*') {
                    //到此为止
                    f[i][j] = f[i][j - 2];
                    //或者本次能配上
                    if (s[i] == p[j - 1] || p[j - 1] == '.')
                        f[i][j] = f[i][j] || f[i - 1][j];
                }
            }
        return f[m][n];
    }
};

第七题

115. 不同的子序列

f[i][j]表示, 前i个位置,能匹配前j个目标的方案数,将位置向后推,

每个新位置的转移方程为,

要么不用这个新位置来匹配,要么用这个新位置匹配,对应两种转移方法。

class Solution {
public:
    int numDistinct(string s, string t) {
        int m = s.length();
        int n = t.length();
        s = " " + s;
        t = " " + t;
        vector> f(m + 1, vector(n + 1, 0));
        //从头到尾都有一种包含“ ”的情况,这是人为插入的,作为起点。
        for (int i = 0; i <= m; i++) f[i][0] = 1;
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= n; j++) {
                //不用s[i] 来匹配t[j]
                f[i][j] = f[i - 1][j];
                //用s[i] 匹配t[j]
                if (s[i] == t[j] && f[i][j] < 2147483647 - f[i - 1][j - 1]) {
                    f[i][j] += f[i - 1][j - 1];
                }
            }
        return f[m][n];
    }
};

第八题 

KMP

28. 实现 strStr()

注释用作提示,需要有基础理解。

class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.length();
        int m = needle.length();
        vector next(m, -1);//下标从0开始,初值-1,从1开始,0
        for (int i = 1, j = -1; i < m; i++) {
            //不匹配就倒退j
            while (j >= 0 && needle[i] != needle[j + 1]) j = next[j];
            //匹配的话可以推
            if (needle[j + 1] == needle[i]) j++;
            //一直匹配一直推,每一步记录一下。
            next[i] = j;
        }
        for (int i = 0, j = -1; i < n; i++) {
            //不匹配就倒退j到相应的前缀位置
            while (j >= 0 && needle[j + 1] != haystack[i]) j = next[j];
            //匹配的话向后推j
            if (j + 1 < m && needle[j + 1] == haystack[i]) j++;
            //成功推到结尾说明匹配成功
            if (j + 1 == m) return i - (m - 1);
        }
        return -1;
    }
};

你可能感兴趣的:(个人笔记极客时间算法训练营,leetcode,算法,职场和发展)