代码手记笔录——字符串

字符串常涉及双指针法有如下几种题型:

  • 先总体反转再局部反转,得到目的字符串
  • 基于 KMP 的算法
  • 基于字符串匹配

先总体反转再局部反转,得到目的字符串

典型题目:

    1. 颠倒字符串中的单词
  • 剑指 Offer 58 - II. 左旋转字符串

补充:先总体反转再局部反转的思想也可应用在二叉树中:226. 翻转二叉树
算法思想是:先序遍历,对当前遍历的节点交换左右节点=>swap(p->left, p->right)

基于 KMP 的算法

参照:https://programmercarl.com/0028.实现strStr.html#其他语言版本

nex 表的理解

模式串next 表即前缀表,记录的是若模式串位置 j 的字符与文本串位置 i 的字符不匹配时应该回退到 next[j] 的位置。next 表记录的是最大公共前-后缀长度

字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。字符串的后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

算法思想:在构造 next 表的时候,比较 i 与 j 元素的意义是:比较前后缀末尾字符是否相同。那不同时应该跳到哪里继续与 i 比较呢,又比较到何时停止比较呢?(while())? 那相同时如何记录呢?

  • 不同时,由于 i-1 与 j-1 相同,所以应该跳到nums 前缀中与nums[j-1]相同的字符的后一个字符。由于 next[j]记录的是长度,与下标多 1 位,所以 j 就刚好跳到nums[j-1] => j = next[j-1]。比较直到始终无法相等j==0或者与某字符相等 => while (j>0 && needle[i] != needle[j])

  • 相同时,就说明前后缀长度增加了 1 度,并且记录在 i 的 next[]位置,说明nums[:i]的最长公共前后缀长度为 j。=> nums[i] = j

next 表的构建代码
void getNext(vector<int> &next, string &needle) {
        int j = 0;
        next[j] = 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;
        } 
}

章节题目总结

459. 重复的子字符串

简单的复杂度方法应该比较容易想到。算法思想:在[1,n/2]内不断地调整预备子字符串的长度,判断预备子字符串能否作为文本串的重复子字符串。时间复杂度为 O(n^2)

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        int n = s.size();
        if (n == 1) 
            return false;
        bool flag;
        for (int i=1; i<=n/2; ++i) {
            string pattern = s.substr(0, i);
            flag = repeatedSubstringPattern(s, pattern);
            if (flag)
                return true;
        }
        return false;
    }
private:
    bool repeatedSubstringPattern(string &text, string &pattern) {
        int tn = text.size(), pn = pattern.size();
        if (tn % pn != 0)
            return false;
        int tp = 0, pp = 0;
        while (tp < tn) {
            if (text[tp] != pattern[pp])
                return false;
            ++tp;
            pp = (pp+1) % pn;
        }
        return true;
    }
};

优化方法是利用 KMP。算法思想:先对字符串 s 构造 next数组。若最后字符的 next 值(next[n-1])不为 0,则说明该字符串具有相同的前缀子串与后缀子串。若 len-next[n-1]能够被 len 整除【若存在重复的子字符串,则其为所求子字符串】,说明存在重复的子字符串,否则不存在。

len-next[n-1] 是重复子字符串,next[n-1] 是s - 重复子字符串的子字符串。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        int n = s.size();
        if (n <= 1)
            return false;
        vector<int> next(n, 0);
        getNext(next, s);
        int sameLen = next[n-1];
        int candT = n-sameLen;
        if (sameLen != 0 && n % candT == 0)
            return true;
        return false;
    }
private:
    void getNext(vector<int> &next, string s) {
        int n = s.size();
        int front = 0;
        next[front] = 0;
        for (int rear=1; rear < n; ++rear) {
            while (front > 0 && s[rear] != s[front])
                front = next[front-1];
            if (s[rear] == s[front])
                ++front;
            next[rear] = front;
        }
    }
};
posted @ 2022-05-09 12:33  MasterBean  阅读(54)  评论(0)    收藏  举报