算法训练营第九天

68 阅读3分钟

151.反转字符串里的单词

class Solution {
public:
    void revese(string& s, int start, int end)
    {
        for(int i = start, j = end; i < j; i++, j--)
        {
            swap(s[i], s[j]);
        }
    }
​
    string reverseWords(string s) {
        // 根据题目条件,单词中会有前导空格和尾随空格,所以反转前应处理多余空格
​
        // 去除首部空格
        int fast = 0;  int slow  = 0;  //定义快慢指针
        while(fast < s.size() && s[fast] == ' ')
        {
            fast++;
        }
​
        // 去除中间多余空格
        for(;fast < s.size(); fast++)
        {
            if(fast - 1 > 0 && s[fast- 1] == s[fast] && s[fast] == ' ')
            {
                continue; // 此处写的时候,没想到要用continue,还是代码功底不够,用了continue可以直接结束判断,执行下一次循环,不用再走else分支
            }
            else{
                s[slow++] = s[fast]; // 不是空格,往前覆盖
            }
        }
​
        // 去除尾部空格
        s.resize(slow); // 注意slow最后是多加了1,如果没有空格,此时大小刚好合适
        if(slow - 1 > 0 && s[slow - 1] == ' ') s.resize(slow - 1);
​
        // 反转字符
        revese(s, 0, s.size() - 1);
​
        // 反转单词,以空格为分割点
        int start = 0;
        for(int i = 0; i <= s.size(); i++)
        {
            if(s[i] == ' ' || i == s.size()) // 遇到空格或到字符末尾
            {
                revese(s, start, i - 1);
​
                start = i + 1;
            }
        }
        return s;
    }
};

关于去除字符串中多余的空格有下面更简洁的写法.

字符串的操作多离不开双指针,本题依然使用双指针

void removeExtraSpace(string& s)
{
    int slow = 0;
    for(int i = 0; i < s.size(); i++)
    {
        if(s[i] != ' ') // 不等于空格才进行操作,若是空格,直接结束判断,i++
        {
            // 对于单词之间要手动添加空格
            if(slow != 0) s[slow++] = ' ';
            
            while(i < s.size() && s[i] != ' ') // 此处一定要用while循环,只有用循环才能保证每次处理的是一整个单词,不然,每个字符之间会有一大推的空格,因为,在处理第一个单词的第二个字符时,slow就不再是零,如果没有while就会在每个字符间有一大堆的空格
            {
                s[slow++] = s[i++];
            }
        }
    }
    
    s.resize(slow);
}

28.实现strStr()

此题涉及到KMP算法。KMP算法用在寻找一个字符串是否在另一个字符串中出现

KMP算法需要构建前缀表。

构建前缀表的思路为:用两个指针指向前缀的最后一个字符和后缀的最后一个字符; 比较两者是否相等

相等在更新前缀表(next数组),不等则向前回溯。

KMP算法实际处理需借助前缀表。

待前缀表构建完成后,即按照前缀表逐字符匹配即可,相同则同时后移,不同则按照前缀表回溯

// 构建前缀表
void getNext(int* next, 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;
        }
    }
    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 - j + 1);
            }
        }
        
        return -1;
    }

459.重复的子字符串

bool repeatedSubstringPattern(string s) {
        // 如何借助前缀表判断是否有最小重复字串
​
        // 如果 next[len - 1] != -1; 就说明有最大公共前后缀
        // len - next[len - 1] 为减去最长公共前后缀的字符串长度
        // len % (len - next[len - 1]) == 0 成立, 则说明有重复子串, 且子串长度就是len - next[len - 1]
​
        // 自己实现KMP算法降低复杂度
        
        if(s.size() == 0) return false;
        int next[s.size()];
        int len = s.size();
        getNext(next,s);
​
        if(next[len - 1] != 0 && len % (len - next[len - 1]) == 0) return true;
​
        return false;
​
​
    }
​
    void getNext(int* next, 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;
        }
    }