字符串part02 ||151.翻转字符串里的单词 卡码网:55.右旋转字符串28. 实现 strStr() 459.重复的子字符串 字符串总结 ●双指针回顾

151.翻转字符串里的单词

erase时应该从后往前,否则删除后不能i++而i–没问题。
s.erase(s.begin() + i); s.erase(s.begin() );而不用 s.erase(0);
s.erase(n);复杂度为O(n)因为要移动后面数组元素。
s.resize(slow);

class Solution {
public:
    string reverseWords(string s) {
        
        while((!s.empty())&&s[0] == ' ')
        {
            s.erase(s.begin());
        }
        
        // for(int i = 1; i < s.size(); i++)
        // {
        //     if((s[i]==' '&&s[i-1]==' ')||(s[i]==' '&& i= s.size()-1))
        //     {
        //         s.erase(i);
        //     }
        // }
        for (int i = s.size() - 1; i > 0; i--) {
        if (s[i] == s[i - 1] && s[i] == ' ') {
            s.erase(s.begin() + i);
        }
        }
        if (s.size() > 0 && s[s.size() - 1] == ' ') {
        s.erase(s.begin() + s.size() - 1);
        }

        for (int left = 0,right = s.size()-1; left < right; left++,right--)
        {
            swap(s[left],s[right]);
        }

        for(int i = 0; i < s.size();i++)
        {
            int slow = i;
            while (i < s.size()&&s[i] != ' ')
            {
                i++;
            }
            for(int j = slow,k = i-1; j<k; j++,k--)
            {
                swap(s[j],s[k]);
            }
            
        }
        return s;


    }
};
void removeExtraSpaces(string& s) {
    int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
    // 去掉字符串前面的空格
    while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
        fastIndex++;
    }
    for (; fastIndex < s.size(); fastIndex++) {
        // 去掉字符串中间部分的冗余空格
        if (fastIndex - 1 > 0
                && s[fastIndex - 1] == s[fastIndex]
                && s[fastIndex] == ' ') {
            continue;
        } else {
            s[slowIndex++] = s[fastIndex];
        }
    }
    if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
        s.resize(slowIndex - 1);
    } else {
        s.resize(slowIndex); // 重新设置字符串大小
    }
}
void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
    int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.html
    for (int i = 0; i < s.size(); ++i) { //
        if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
            if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
            while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                s[slow++] = s[i++];
            }
        }
    }
    s.resize(slow); //slow的大小即为去除多余空格后的大小。
}

遇到不是空格的才进行处理,处理下一个时把前一个单词之后的空格补上。

class Solution {
public:
    void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
        int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.html
        for (int i = 0; i < s.size(); ++i) { //
            if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
                if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。
    }

    string reverseWords(string s) {
        removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s, 0, s.size() - 1);
        int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。
        for (int i = 0; i <= s.size(); ++i) {
            if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。
                reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。
                start = i + 1; //更新下一个单词的开始下标start
            }
        }
        return s;
    }
};

注意代码封装将移除、翻转等功能进行封装。

卡码网:55.右旋转字符串

#include
#include
using namespace std;
int main() {
int n;
string s;
cin >> n;
cin >> s;
int len = s.size(); //获取长度

reverse(s.begin(), s.end()); // 整体反转
reverse(s.begin(), s.begin() + n); // 先反转前一段,长度n
reverse(s.begin() + n, s.end()); // 再反转后一段

cout << s << endl;

} #include
#include
using namespace std;
int main() {
int n;
string s;
cin >> n;
cin >> s;
int len = s.size(); //获取长度

reverse(s.begin(), s.end()); // 整体反转
reverse(s.begin(), s.begin() + n); // 先反转前一段,长度n
reverse(s.begin() + n, s.end()); // 再反转后一段

cout << s << endl;

}

28. 实现 strStr()

当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
什么是KMP
说到KMP,先说一下KMP这个名字是怎么来的,为什么叫做KMP呢。
因为是由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP
要在文本串:aabaabaafa 中查找是否出现过一个模式串:aabaaf。
主串不回溯!!!!!!
模式串的循环j主要取决于模式串的结构
j值的多少取决于当前字符之前的串的前后缀的相似度
模式串与主串存在较多部分匹配
文章中字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。

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

代码理解:KMP算法代码理解
原理理解:大话数据结构
c++实现:比较简洁的实现,nextval数组右移第一位记成0
next数组有三种表示方法,减一右移不减1.。。。。。。

    void getNext(int* next,const string&s)
    {
        int j = -1 , i = 0;
        next[0] = -1;
        int n = s.size();
        while (i < n-1)
        {
            if(j == -1||s[i] == s[j])
            {
                next[++i] = ++j;
            }else
            {
                j = next[j];
            }
        }   
    }
    void getNext_2(int* next,const string&s)
    {
        int j = 0 , i = 0;//j赋值为0,next[j]数组存的数都大了1
        next[0] = 0;
        int n = s.size();
        while (i < n-1)
        {
            if(j == 0||s[i] == s[j-1])//其实就是s[i] == s[next[i]-1]因为s[i]现在储存的大了1所以是j-1
            {
                ++i;
                ++j;
                next[i] = j;

            }else
            {
               j=next[j-1]; //其实就是j=next[next[i]-1];
            }
        }   
    }
void GetNextval(SqString t,int nextval[])  
//由模式串t求出nextval值
{
	int j=0,k=-1;
	nextval[0]=-1;
   	while (j<t.length-1) 
	{
       	if (k==-1 || t.data[j]==t.data[k]) 
		{	
			j++;k++;
			if (t.data[j]!=t.data[k]) //因为如果匹配不上就会跳转k因此先判断要跳转的k是不是和j位置字符相同,如果相同肯定匹配不上就会跳转k的next因此还不如直接用nextk填充nextj

				nextval[j]=k;
           	else  
				nextval[j]=nextval[k];

       	}
       	else  k=nextval[k];    	
	}

}
    void getNext(int* next,const string&s)
    {
        int j = -1 , i = 0;
        next[0] = -1;
        int n = s.size();
        while (i < n-1)
        {
            if(j == -1||s[i] == s[j])
            {
            	++i;
            	++j;
            	if(s[i]!=s[j])
                   next[i] = j;
                else next[i] = next[j];
            }else
            {
                j = next[j];
            }
        }   
    }
#include <vector>
// @lc code=start
class Solution {
public:
    // void getNext(int* next,const string&s)
    // {
    //     int j = -1 , i = 0;
    //     next[0] = -1;
    //     int n = s.size();
    //     while (i < n)
    //     {
    //         if(j == -1||s[i] == s[j])
    //         {
    //             next[++i] = ++j;
    //         }else
    //         {
    //             j = next[j];
    //         }
    //     }   
    // }
    // void getNext_2(int* next,const string&s)
    // {
    //     int j = 0 , i = 0;
    //     next[0] = 0;
    //     int n = s.size();
    //     while (i < n-1)
    //     {
    //         if(j == 0||s[i] == s[j-1])
    //         {
    //             ++i;
    //             ++j;
    //             next[i] = j;

    //         }else
    //         {
    //            j=next[j-1]; 
    //         }
    //     }   
    // }
    void getNext_3(int& nextval,const string&s)
    {
        int n = s.size();
        int i = 0, j = -1;
        nextval[0] = -1;
        while (i<n)
        {
            if(j==-1||s[i]==s[j])
            {
                i++;
                j++;
                //if(i>n)  nextval[i] = j;
                //else if( s[i]!=s[j]) nextval[i] = j;
                if( s[i]!=s[j]) nextval[i] = j;
                else nextval[i] = nextval[j];
            }else
            {
                j = nextval[j];
            }
        }
    }

    int strStr(string haystack, string needle) {
        vector<int> nextval(needle.size()+1);
        int ans = -1;
        getNext_3(&nextval[0],needle);
        int n = haystack.size();
        int m = needle.size();
        // for (int i = 0,j = 0; i < n; i++)
        // {
        //     while (j>0 && haystack[i] != needle[j])
        //     {
        //         j = nextval[j];
        //     }
        //     if(j == -1 || haystack[i] == needle[j])
        //     {
        //         j++;
        //     }
        //     if(j == m)
        //     {
        //         ans = i-j+1;
        //         return ans;
        //         // ans.push_back(i-j+1);
        //         // j = nextval[j];
        //     }            
        // }
        int i = 0 , j = 0;
        while ( i<n )
        {
            if(j == -1 || haystack[i] == needle[j])
            {
                i++;
                j++;

            }else
            {
                j = nextval[j];
            }
            if(j == m)
            {
                ans = i - m;//注意就不能 (i-j+1)因为和for循环不同i已经++了
                return ans;
        //         // ans.push_back(i-j+1);
        //         // j = nextval[j];
            }
        }
        
        return ans;
    }
};

终于搞懂了KMP用时两天。坑:不同人next数组表示不同!!!!!!!!!!

459.重复的子字符串

所以判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。
当然,我们在判断 s + s 拼接的字符串里是否出现一个s的的时候,要刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s,我们要搜索的是中间拼接出来的s。
iterator erase (iterator p);
string& erase(size_t pos=0, size_t len = npos);
iterator erase (iterator first, iterator last);
len%(len - next[j])==0

class Solution {
public:
    void getNext(int* next,const string&s )
    {
        int n = s.size();
        int i = 0, j = -1;
        next[0] = -1;
        while (i<n)
        {
            if(j == -1 || s[i] == s[j]) next[++i] = ++j;
            else j = next[j];
        }
        
    }
    bool repeatedSubstringPattern(string s) {
        // string t = s + s;O(n+m)
        // t.erase(t.begin()); t.erase(t.end() - 1); // 掐头去尾
        // if (t.find(s) != string::npos) return true; // r
        // return false;//string中find()返回值是字母在母串中的下标位置。如果没有找到,那么会返回一个特别的标记npos,一般写作string::npos
        int len = s.size();
        int next[len+1];
        getNext(next,s);
        if(next[len] != -1&&next[len] != 0&&len%(len-next[len]) == 0){
            return true;
        }
        return false;
        
    
    }
};
  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值