leetcode刷题之字符串(反转,KMP,KMPwocaonima)

一、字符串之反转

1.344 反转字符串

  1. 反转字符串:
  • reverse(str.begin(), str.end());

  1. 反转数组:
  • int a[5] = {1, 2, 3, 4, 5};
    reverse(a, a+5);//n为数组中的元素个数

    这题的本意当然是不用库函数,怎么做:双指针思想

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

总结:

  • 字符串的长度:s.length();
  • 字符数组的长度:s.size();
  • 利用string头文件中的strlen()函数:strlen(str)
  • C++的strlen(str)和str.length()和str.size()都可以求字符串长度。其中str.length()和str.size()是用于求string类对象的成员函数,strlen(str)是用于求字符数组的长度,其参数是char*。
  • 这题里面的str是放在了vector字符数组里面,所以用size();

2.541 反转字符串 II

难度:简单

class Solution {
    
    
public:
    string reverseStr(string s, int k) 
    {
    
    
        int length=s.length();
        
        for(int j=0;j<length;j+=(2*k))//j=0,2k,4k....      
        {
    
    
            if(length-j<k)//如果剩余字符少于 k 个,则将剩余字符全部反转。
            {
    
    
                reverse(s.begin()+j,s.end());
            }
            else if((length-j<2*k)&&(length-j>=k))
            //如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
            {
    
    
                reverse(s.begin()+j,s.begin()+j+k);
            }
            else 
            {
    
    
                reverse(s.begin()+j,s.begin()+j+k);
            }
        }
        return s;
    }
};

总结:

  • begin
    解释:begin()函数返回一个迭代器,指向字符串的第一个元素.

  • end
    解释:end()函数返回一个迭代器,指向字符串的末尾(最后一个字符的下一个位置).

二、字符串之双指针

1.剑指 Offer 05 替换空格:请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

  • 方法一:replace
    找有多少个空格,在有空格的位置用replace
class Solution {
    
    
public:
    string replaceSpace(string s) 
    {
    
      
        for (int i = 0; i < s.length(); i++) 
        {
    
    
            if (s[i] == ' ') 
            {
    
    
                s.replace(i, 1, "%20");
            }
        }
        return s;
    }
};

  • 方法二:双指针
    首先扩充数组到每个空格替换成"%20"之后的大小。然后从后向前替换空格,也就是双指针法,j指向新长度的末尾,i指向旧长度的末尾。
    问题:从前向后填充不行么?
    从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。
    这么做有两个好处:
    (1)不用申请新数组。
    (2)从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
class Solution {
    
    
public:
    string replaceSpace(string s) 
    {
    
      
        int size=s.length();
        int count=0;
        for(int i=0;i<s.length();i++)
        {
    
    
            if(s[i]==' ')
            {
    
    
                count++;//有多少空格
            }
        }
        
        s.resize(2*count+size,' ');//扩充
        int size_new=s.length();
      
        for(int i=size-1,j=size_new-1;i<j;i--,j--)//j是新字符串的尾端
        {
    
    
            if(s[i]!=' ')
            {
    
    
                s[j]=s[i];
            }
            else
            {
    
    
                s[j]='0';
                s[j-1]='2';
                s[j-2]='%';
                j=j-2;
            }
            
        }
        return s;
    }
};

三、字符串之匹配KMP算法

举个例子:要在文本串:aabaabaafa 中查找是否出现过一个模式串:aabaaf。

  1. 先在模式串里面找连续子串。
    前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
    后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
    在这些子串里面找最长相等前后缀。

  2. 得到前缀表(前缀表里面放的是最长相等前后缀的长度),前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。

子串 最长相同前后缀的长度
a 0
aa 1
aab 0
aaba 1
aabaa 2
aabaaf 0
  1. 找到的不匹配的位置, 那么此时我们要看它的前一个字符的前缀表的数值是多少。
    比如aabaaf里面不匹配的位置是f,他前一个字符的前缀表的数值是2,所以把下标移动到文本串下标2的位置继续比配。
    最后就在文本串中找到了和模式串匹配的子串了

代码:

  1. 先构造next数组:
    (1)初始化:定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j),所以next[0]=j;
int j = -1;
next[0] = j;

(2)处理前后缀不相同的情况:如果 s[i] 与 s[j+1]不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回退。
怎么回退呢?next[j]就是记录着j(包括j)之前的子串的相同前后缀的长度。那么 s[i] 与 s[j+1] 不相同,就要找 j+1前一个元素在next数组里的值(就是next[j])。
(3)处理前后缀相同的情况:如果 s[i] 与 s[j + 1] 相同,那么就同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。

void getNext(int* next, const string& s) {
    
    
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) {
    
    
            while (j > 0 && s[i] != s[j]) {
    
     // j要保证大于0,因为下面有取j-1作为数组下标的操作
                j = next[j - 1]; // 注意这里,是要找前一位的对应的回退位置了
            }
            if (s[i] == s[j]) {
    
    
                j++;
            }
            next[i] = j;
        }
    }
  1. 使用next数组来做匹配
class Solution 
{
    
    
public:
    void getNext(int* next, const string& s) 
    {
    
    
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) //注意i从1开始
        {
    
    
            while (j > 0 && s[i] != s[j]) //前后缀不相同了
            {
    
    
                j = next[j - 1];
            }
            if (s[i] == s[j]) // 找到相同的前后缀
            {
    
    
                j++;
            }
            next[i] = j;// 将j(前缀的长度)赋给next[i]
        }
    }
    //下面用next数组做匹配
    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++) // 注意i就从0开始
        {
    
    
            while(j > 0 && haystack[i] != needle[j]) 
            {
    
    
                j = next[j - 1];
            }
            if (haystack[i] == needle[j]) 
            {
    
    
                j++;
            }
            if (j == needle.size() )// 文本串s里出现了模式串t 
            {
    
    
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }
};

猜你喜欢

转载自blog.csdn.net/mabaizi/article/details/128751898