输入一个英文句子,翻转句子中单词的顺序,但单词内字符顺序不变



面试题:翻转单词顺序VS左旋转字符串
题目一:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串 "I am a student",则输出 "student.a am I " 。
题目二:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如输入字符串 "abcdefg" 和数字2,该函数将返回左旋转2位得到的结果 "cdefgab"。


这个题目流传甚广,很多公司都多次拿来作面试题,很多应聘者也多次在各种博客或者书籍上看到过通过两次翻转字符串的解法,于是很快就可以跟面试官解释清楚解题思路:第一步翻转句子中所有的字符。比如翻转”I am a student.”中所有的字符得到”.tneduts a ma I”,此时不但翻转了句子中单词的顺序,连单词内的字符顺序也被翻转了。第二步再翻转每个单词中字符的顺序,就得到了”student a am I”。这正是符合题目要求的输出。
这种思路的关键在于实现一个函数以翻转字符串中的一段。下面的函数reverse可以完成这一功能:


void Reverse (char*pBegin, char *pEnd)
{
    if ( pBegin == NULL  ||  pEnd == NULL )
        return;

    while ( pBegin < pEnd )
    {
        char temp = *pBegin;
        *pBegin = *pEnd;
        *pEnd = temp;

        pBegin++;

        pEnd--;
    }
}
_____________________________________________________________________

接着我们可以用这个函数先翻转整个句子,再翻转句子中的每个单词。这种思路的参考代码如下:
char*ReverseSentence(char *pData)
{
    if (pData == NULL)
        return NULL;

    char  *pBegin=pData;

    char*pEnd=pData;


    while(*pEnd!=' \0')
        pEnd++;
    pEnd--;

    // 翻转整个句子
    Reverse (pBegin,  pEnd);

    //翻转句子中的每个单词
    pBegin = pEnd = pData;


    while(*pBegin!='\0')
    {
        if ( *pBegin == ' ' )
        {
            pBegin++;
            pEnd++;
        }
        else if(*pEnd == ' '  | |  *pEnd == '\0')
        {
 
            Reverse (pBegin, --pEnd);
            pBegin = ++pEnd;
        }
        else
        {
            pEnd++;
        }

        return pData;
}


       在英语句子中,单词被空格符号分隔,因此我们可以通过扫描空格来确定每个单词的起始和终止位置。在上述代码的翻转每个单词阶段,指针pBegin指向单词的第一个字符,而pEnd指向单词的最后一个字符。

        有经验的面试官看到一个应聘者几乎不假思索就能想出一种比较巧妙的算法,就会觉得他之前可能见过这个题目。这个时候很多面试官都会再问一个问题,以考查他是不是真的理解了这种算法。面试官一个常见的考查办法就是问一个类似的但更加难一点的问题。以这道题为例,如果面试官觉得应聘者之前看过这个思路,那他可能再问第二个问题:
        题目二:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如输入字符串 "abcdefg "和数字2,该函数将返回左旋转2位得到的结果 " cdefgab "。
       要找到字符串旋转时每个字符移动的规律,不是一件轻易的事情。那我们是不是可以从解决第一个问题的思路中找到启发?在第一个问题中,如果输入的字符串之中只有两个单词,比如”hello world”,那么翻转这个句子中的单词顺序就得到了”world hello”。比较这两个字符串,我们是不是可以把”world hello”看成是把原始字符串”hello world”的前面若干个字符转移到后面?也就是说这两个问题是非常相似的,我们同样可以通过翻转字符串的办法来解决第二个问题。
        以”abcdefg”为例,我们可以把它分为两部分。由于想把它的前两个字符移到后面,我们就把前两个字符分到第一部分,把后面的所有字符都分到第二部分。我们先分别翻转这两部分,于是就得到”bagfedc”。接下来我们再翻转整个字符串,得到的”cdefgab”刚好就是把原始字符串左旋转2位的结果。
        通过前面的分析,我们发现只需要调用3次前面的Reverse函数就可以实现字符串的左旋转功能。参考代码如下:


char*  leftRotateString (char* pStr,  int n)
{
    if (pStr  !=NULL)
    {
        int nLength=static_cast<int> (strlen (pStr));


        if ( (nLength>0) &&  (n>0) && (n<nLength))
        {
            char *pFirstStart = pStr;
            char *pFirstEnd = pStr+n-l;
            char *pSecondStart = pStr+n;
            char *pSecondEnd = pStr+nLength -  1 ;

            // 翻转字符串的前面n个字符
            Reverse (pFirstStart,  pFirstEnd);
            // 翻转字符串的后面部分
            Reverse(pSecondStart,  pSecondEnd);
            // 翻转整个字符串
            Reverse (pFirstStart,  pSecondEnd);
         }
   }
    return p Str;
}
_____________________________________________________________________

想清楚思路之后再写代码是一件很容易的事情,但我们也不能掉以轻心。面试官在检查与字符串相关的代码时经常会发现两种问题:一是输入空指针NULL时程序会崩溃;二是内存访问越界的问题,也就是试图访问不属于字符串的内存。例如如果输入的n小于0,指针pStr+n指向的内存就不属于字符串。如果我们不排除这种情况,试图访问不属于字符串的内存就会留下严重的内存越界的安全隐患。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值