剑指offer -- 翻转字符串(翻转单词顺序(一个空格 + 多个空格)+ 左旋转字符串)

题目一:翻转单词顺序

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变s。为了简单起见,标点符号和普通字母一样处理。例如输入字符串“I am a student.”,则输出“student. a am I"。

我们可以通过两步来解决这个问题,第一步翻转句子中的所有字符,将“I am a student.”翻转成“.tneduts a am I”,此时不但翻转了句子中单词的顺序,连单词内的字符顺序也被翻转了,第二步再翻转每个单词中字符的顺序,就可以得到“student. a am I"。这种思路的关键在于实现一个函数用以翻转字符串中的字符。

解法一:

void Reverse(char *begin,char *end){
    
    
    while(begin<end){
    
    
        char temp = *begin;
        *begin = *end;
        *end = temp;

        begin++;
        end--;
    }
}

char* reverseWords(char* s){
    
    
    if(s == NULL) return s;

    char *begin = s;
    char *end = s;
    while(*end != '\0'){
    
    
        end++;
    }
    end--;
    Reverse(begin,end);
    begin = end = s;

    while(*begin != '\0'){
    
    
        if(*begin == ' '){
    
    
            begin++;
            end++;
        }
        else if(*end == ' '||*end == '\0')
        {
    
    
            Reverse(begin,--end); //此处为--end 先 -- 回到空格的前一个字符
            begin = ++end;        //此处为++end 先 ++ 回到空格
        }
        else{
    
    
            end++;
        }
    }
    return s;
}

解法二:
这个题目的本质就是先返回句子后面的单词再返回句子前面的单词,每个单词由空格分隔。如果我们能从前向后依次得到所有的单词将他们放入栈中,然后再弹出栈顶拼接,就完成了任务。istringstream 类用于执行C++风格的串流的输入操作,它用空格作为字符串分隔符。

#include<iostream>  
#include<sstream>        //istringstream 必须包含这个头文件
#include<string>  
using namespace std;  
int main()  
{
    
      
	string str = "  abc d   11 1.23";  
	istringstream is(str);  

	string s;  
	is>>s;
	cout<<s<<endl;

	char q;
	is>>q;
	cout<<q<<endl;

	int w;
	is>>w;
	cout<<w<<endl;

	double t;
	is>>t;
	cout<<t<<endl;

	return 0;
}

在这里插入图片描述

 string reverseWords(string s) 
    {
    
    
        stack<string> stk;
        string res, temp;
        istringstream ss(s); // ss与数输入的字符串s绑定
        while (ss >> temp)   // 以空格为分隔符将其分割
        {
    
    
            stk.push(temp);
            stk.push(" ");
        }
        if(!stk.empty()) 
            stk.pop();       // 弹出最后压入的那个多余空格
        while (! stk.empty())// 单词翻转后的新串
        {
    
    
            res += stk.top();
            stk.pop();
        }
        return res;
    }

此题的题目中是正常的英文句子写法,即每个单词之间由一个空格间隔且开头和结尾均没有多余的空格,如果开头和结尾有多余的空格,两个单词之间也不止一个空格,但是要求处理完以后返回的翻转字符是正常格式,即开头结尾没有多余空格,两个单词之间由一个空格间隔,我们又该怎样处理呢?方法二由于我们借助了istringstream类可以帮我们直接提取字符串,而不用担心多余的空格,那么方法一就需要我们先实现一个删除字符串中多余空格的函数,再进行接下来的那两个步骤。

void deletespace(char *s){
    
    
	char *begin = s;
	char *newstr = s;

	while(*begin == ' '){
    
     //去除开头的空格
		begin++;
	}
	while(*begin != '\0'){
    
    
		if(*begin ==' '){
    
    
			*newstr = ' ';
			while(*begin == ' ')
			begin++;
		}
		else
		{
    
    
			*newstr = *begin;
			begin++;
		}
		newstr++;
	}
	newstr--;           //如果最后有多余的空格那么*begin=' '会导致*newstr的变成' '
	if(*newstr != ' ')  //需要判断最后一个字符是否为' '
		newstr++;
		
	*newstr = '\0';
}

题目二:左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串“abcdefg”和2,该函数将返回左旋转两位得到的结果“cdefgab"。
这道题其实就是将前面长度为n的字符截取后拼接到后面,c++中有一个函数substr是专门用来截取字符串的,我们如果应用这个函数来解这道题虽然代码简洁,但是意义不大。

(substr函数:https://blog.csdn.net/scarificed/article/details/117229978)

string reverseLeftWords(string s, int n) {
    
    
        int nlength = s.length();
        string ans1 = s.substr(0,n);
        string ans2 = s.substr(n,nlength-1);
        return ans2+ans1;

    }

在上面的题目中,如果输入的字符串只有两个单词,比如说"hello world",那么翻转后变成了"world hello",比较这两个字符串,我们是不是可以把"world hello"看成是"hello world"的前面若干个字符移到后面。也就是说这两个问题是相似的,我们同样可以通过翻转字符串的办法来解决第二个问题。例如字符串“abcdefg”,我们可以把它分成两部分,“ab”和“cdefg”首先分别翻转这两个部分,使原字符串变成“bagfedc”,然后在进行翻转“cdegfab"。只需要调用3次我们已经写好的Reverse函数即可。

char* reverseLeftWords(char* s, int n){
    
    
	if(s!=NULL){
    
    
		int length = 0;
		char *first = s;

		while(*first != '\0'){
    
    
			length++;
			first++;
		}

		if(length > 0 && n > 0 && n < length){
    
    
			first = s;
			char *firstend = s+n-1;
			char *second = s+n;
			char *secondend = s+length-1;

			Reverse(first,firstend);
			Reverse(second,secondend);
			Reverse(first,secondend);

		}
	}

	return s;
}

Guess you like

Origin blog.csdn.net/scarificed/article/details/120702662