代码随想录刷题顺序 字符串

目录

344. 反转字符串

541. 反转字符串 II

剑指Offer 05.替换空格

151. 反转字符串中的单词

剑指Offer58-II.左旋转字符串

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

459. 重复的子字符串


344. 反转字符串

双指针

class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int i = 0, j = s.size() - 1; i < s.size() / 2; i++, j--)
        {
            char tmp;
            tmp = s[i];
            s[i] = s[j];
            s[j] = tmp;
        }
    }
};

541. 反转字符串 II

本题是将数组拆分成一个个2*k大小的小组,所以可以直接让i+2*k

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += 2 * k)
        {
            if (i + k < s.size())
            {
                reverse(s.begin() + i, s.begin() + i + k);
            }else
            {
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

剑指Offer 05.替换空格

题目链接:替换空格__牛客网

仍然使用双指针,可以先将数组扩充到替换成%20后需要的大小,之后从后往前遍历数组,双指针一个i指向扩充后数组末端,一个j指向原数组末端

class Solution {
public:
	void replaceSpace(char *str,int length) {	
		int count = 0; //记录空格数
        for (int i = 0;i < length; i++){
            if (str[i] == ' ')
                count++;
        }

		int length2 = length + count * 2; //扩充后数组长度
		for (int j = length - 1, i = length2 - 1; j >= 0; j--, i--)
		{
			if (str[j] != ' ')
			{
				swap(str[j], str[i]);
			}else
			{
				str[i--] = '0';
				str[i--] = '2';
				str[i] = '%';
			}
		}
	}
};

151. 反转字符串中的单词

大体思路是可以先去除原字符串中所有多余的空格,之后再将整个字符串反转,最后将每个单词反转回来。

去除多余空格思路:

最好不要用erase来直接去掉空格,因为erase本身的时间复杂度就是O(n),再遍历一遍字符串之后复杂度将会变为O(n*n)了。

所以可以采用快慢指针的方法来做,这里去除空格的思路与之前数组题中的一道题一致:27. 移除元素

class Solution {
public:
    void RemoveSpc(string &s)
    {
        int slow = 0; //慢指针:指向新数组的末尾
        for (int fast = 0; fast < s.size(); fast++)
        {
            if (s[fast] != ' ')
            {
                if (slow != 0) //slow!=0说明此时不是第一个单词,所以要在其末尾补上一个空格
                {
                    s[slow++] = ' '; 
                }
                while (fast < s.size() && s[fast] != ' ')
                {
                    s[slow++] = s[fast++];
                }
            }
        }
        s.resize(slow); //重新设置数组大小
    }

    void reverse(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) 
    {
        RemoveSpc(s);
        reverse(s, 0, s.size() - 1);

        int start = 0; //记录一个单词的开始位置1
        for (int i = 0; i <= s.size(); i++)
        {
            if (s[i] == ' ' || i == s.size()) //如果遇到空格则说明已经到了一个单词的结束位置,且若i到了数组的末尾,说明已经到了最后一个单词位置
            {
                reverse(s, start, i - 1);
                start = i + 1; //更新
            }
        }

        return s;
    }
};

剑指Offer58-II.左旋转字符串

(找不到练习这题的地方qaq)

和上题一样可以通过局部反转以及全局反转来达成目的。

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(), s.begin() + n);
        reverse(s.begin() + n, s.end());
        reverse(s.begin(), s.end());
        return s;
    }
};

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

KMP算法

class Solution {
public:
    void getNext(int *next, string needle)
    {
        //初始化
        int j = 0; 
        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++; //j++后可表示为前后缀相等的长度
            }

            //更新next
            next[i] = j;
        }
    }

    int strStr(string haystack, string needle) 
    {
        if (needle.size() == 0)
        {
            return 0;
        }

        int next[needle.size()];
        getNext(next, needle);

        for (int i = 0, j = 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;
    }
};

459. 重复的子字符串

KMP:

下方分别是原字符串s,最长相等前缀t,最长相等后缀k(按照下方的推理过程,这个后缀一定是从s中最后一个字符开始的,即应该是len-1这个位置的next)

最长相等前后缀不包含的子串就是最小重复子串,如下图s[0]s[1]

步骤1:因为t和k为最长相等前后缀,所以t[0] = k[0],所以根据上图位置可推知s[0] = s[2];又因为t[1] = k[1],所以s[1] = s[3],所以s[0]s[1] = s[2]s[3]

步骤2:因为t[2]和k[0],t[3]和k[1]在s中位置相等,所以可知t[2]t[3] = k[0]k[1] (不太懂这步的在推理中的意义)

步骤3:与步骤1同理,可知s[2]s[3] = s[4]s[5]

最后反复上诉步骤,得到s[0]s[1] = s[2]s[3] = s[4]s[5]

数学推导:

假设s由多个长度为x的n个子串组成,所以s的len = n * x

因为最长相等前后缀不包含的子串就是最小重复子串,所以最长相等前后缀的长度为m * x,且m = n + 1

所以可知若nx % (n - m)x = 0,就可以判定有重复出现的子字符串

如果 next[len - 1] != 0,则说明字符串有最长相同的前后缀(前缀是从s的第一个字符开始,后缀从s的最后一个字符开始)

所以如果len % (len - (next[len - 1])) == 0 ,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。

class Solution {
public:
    void getNext(int *next, string needle)
    {
        int j = 0;
        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;
        }
    }

    bool repeatedSubstringPattern(string s) 
    {
        int next[s.size()];
        getNext(next, s);

        int len = s.size();
        if (next[len - 1] != 0 && len % (len - next[len - 1]) == 0)
        {
            return true;
        }
        return false;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值