如何在不使用内置函数的情况下反转句子中的单词?

时间:2014-08-13 03:48:01

标签: c++ arrays reverse sentence

这是面试问题:

  

如何将 Dogs like cats 转换为 cats like Dogs

我的代码显示: cats like cats 。我在哪里犯错误?

#include <iostream>
using namespace std;

int main()
{
    char sentence[] = ("dogs like cats");
    cout << sentence << endl;

    int len = 0;

    for (int i = 0; sentence[i] != '\0'; i++)
    {
        len++;
    }
    cout << len << endl;

    char reverse[len];
    int k = 0;

    for (int j = len - 1; j >= 0; j--)
    {
        reverse[k] = sentence[j];
        k++;
    }

    cout << reverse << endl;

    int words = 0;
    char str[len];

    for (int l = 0; reverse[l] != '\0'; l++)
    {
        if (reverse[l] == ' ' || reverse[l] == '\0') // not sure about this part
        {
            for (int m = l; m >= 0; m--)
            {
                str[words] = reverse[m];
                words++;
            }
        }
    }

    cout << str;

    return 0;
}

我知道你可以使用指针,堆栈,向量来做到这一点......但是面试官对此并不感兴趣!

5 个答案:

答案 0 :(得分:4)

这是示例代码的固定版本:

  • 您的主要问题是,每次找到并' ''\0'时,您都会将反向字符串的字节从开头复制到该点。 loop 5中的示例,您从索引0-5(stac)以相反的顺序从reverse复制到str,但在loop 10中从索引0复制-10(stac ekil)从reversestr的顺序相反,直到此处您已经打印了结果字符串(&#39;猫像猫一样),并且相同在loop 15中所有这些递增str的索引,在你写的最后一个循环中传递str的有效内存的末尾(并且因为它没有作为输出打印)。
  • 你需要在结束最后一个单词时保持跟踪,只反转实际单词,而不是从开头到实际索引的字符串。
  • 你不想在翻译单词时计算特殊字符(&#39;&#39;&#39; \ 0&#39;),你会以{{1}结尾}

提供的修改后的示例代码:

cats like\0dogs

有些代码是使用语言最简单的功能限​​制而写的。

#include <iostream>
using namespace std;

int main() {
    char sentence[] = ("dogs like cats");
    cout << sentence << endl;

    int len = 0;

    for (int i = 0; sentence[i] != '\0'; i++) {
        len++;
    }
    cout << len << endl;

    char reverse[len];
    int k = 0;

    for (int j = len - 1; j >= 0; j--) {
        reverse[k] = sentence[j];
        k++;
    }

    cout << reverse << endl;

    int words = 0;
    char str[len];

    // change here added last_l to track the end of the last word reversed, moved
    // the check of the end condition to the end of loop body for handling the \0
    // case
    for (int l = 0, last_l = 0; ; l++) {
        if (reverse[l] == ' ' || reverse[l] == '\0')
        {
            for (int m = l - 1; m >= last_l; m--) { // change here, using last_t to
                str[words] = reverse[m];            // only reverse the last word
                words++;                            // without the split character
            }
            last_l = l + 1;                         // update the end of the last
                                                    // word reversed
            str[words] = reverse[l];                // copy the split character
            words++;
        }
        if (reverse[l] == '\0')                     // break the loop
            break;
    }

    cout << str << endl;

    return 0;
}

答案 1 :(得分:2)

将您的算法分解成碎片。首先,您找到字符串的长度,不包括null char终止符。这是正确的,但可以简化。

size_t len = 0;
for (int i = 0; sentence[i] != '\0'; i++) {
    len++;
}
cout << len << endl;

这很容易写成:

size_t len = 0;
while (sentence[len])
    ++len;

接下来,您将反转整个字符串,但是第一个缺陷曲面。您在此处声明的VLA(可变长度数组)(您不需要也不应该使用,因为它是C ++扩展且非标准的)不会考虑或设置终止空值-char。

char reverse[len]; // !! should be len+1
int k = 0;
for (int j = len - 1; j >= 0; j--) {
    reverse[k] = sentence[j];
    k++;
}
// !! Should have reverse[k] = 0; here.
cout << reverse << endl; // !! Undefined-behavior. no terminator.

此临时缓冲区字符串根本不需要。没有理由你不能就地进行整个操作。一旦我们正确计算len,您只需执行以下操作即可反转整个序列,从而将null char终止符保留在正确的位置:

// reverse entire sequence
int i = 0, j = len;
while (i < j--)
{
    char c = sentence[i];
    sentence[i++] = sentence[j];
    sentence[j] = c;
}

接下来,我们转到您尝试撤消每个内部单词的位置。同样,与以前一样,缓冲区长度不正确。它应该是len+1。更糟糕的是(很难想象),在找到单词的结束点时,你永远不会记得离开的位置。该位置应该是您开始检查的 next 点,并跳过空格。不保留您从当前点一直复制回字符串的开头。这基本上会导致cats超过dogs

int words = 0;
char str[len]; // !! should be len+1
for (int l = 0; reverse[l] != '\0'; l++) 
{
    if (reverse[l] == ' ' || reverse[l] == '\0') // not sure about this part
    {
        for (int m = l; m >= 0; m--) {
            str[words] = reverse[m];
            words++;
        }
    }
}
cout << str; //!! Undefined behavior. non-terminated string.

再一次,这可以毫无困难地就地完成。一个这样的算法看起来像这样(并注意到反转实际单词的循环不是巧合地与反转整个缓冲区的算法相同):

// walk again, reversing each word.
i = 0;
while (sentence[i])
{
    // skip ws; root 'i' at beginning of word
    while (sentence[i] == ' ') // or use std::isspace(sentence[i])
        ++i;

    // skip until ws or eos; root 'j' at one-past end of word
    j = i;
    while (sentence[j] && sentence[j] != ' ') // or use !std::isspace(sentence[j])
        ++j;

    // remember the last position
    size_t last = j;

    // same reversal algorithm we had before
    while (i < j--)
    {
        char c = sentence[i];
        sentence[i++] = sentence[j];
        sentence[j] = c;
    }

    // start at the termination point where we last stopped
    i = last;
}

全部放在一起

虽然使用指针比使用所有这些索引变量简单得多,但以下内容将完成您正在尝试的操作。

#include <iostream>

int main()
{
    char s[] = "dogs like cats";
    std::cout << s << '\n';

    size_t len = 0, i, j;
    while (s[len])
        ++len;

    // reverse entire sequence
    i = 0, j = len;
    while (i < j--)
    {
        char c = s[i]; // or use std::swap
        s[i++] = s[j];
        s[j] = c;
    }

    // walk again, reversing each word.
    i = 0;
    while (s[i])
    {
        // skip ws; root 'i' at beginning of word
        while (s[i] == ' ') // or use std::isspace
            ++i;

        // skip until ws or eos; root 'j' at one-past end of word
        j = i;
        while (s[j] && s[j] != ' ') // or use !std::isspace
            ++j;

        // remember the last position
        size_t last = j;
        while (i < j--)
        {
            char c = s[i]; // or use std::swap
            s[i++] = s[j];
            s[j] = c;
        }

        // start at last-left posiion
        i = last;
    }

    std::cout << s << '\n';
    return 0;
}

<强>输出

dogs like cats
cats like dogs

答案 2 :(得分:0)

我的建议是将原始字符串分解为一个单词数组,反转该数组。然后将这些单词添加到你的反向句子中,中间有一个空格。

答案 3 :(得分:0)

由于他们没有要求库,我假设没有std::string,没有vectors,一无所有,所以我用C语言写了......唯一使用的是printf。其他一切都是从头开始:l

这个想法是你首先反转数组。然后按空格分割数组并反转每个单词。

示例:http://ideone.com/io6Bh9

代码:

#include <stdio.h>

int strlen(const char* s)
{
    int l = 0;
    while (*s++) ++l;
    return l;
}


void reverse(char* str)
{
    int i = 0, j = strlen(str) - 1;

    for(; i < j; ++i, --j)
    {
       str[i] ^= str[j];
       str[j] ^= str[i];
       str[i] ^= str[j];
    }
}

void nulltok(char* str, char tok, int* parts)
{
    int i = 0, len = strlen(str);
    *parts = 1;

    for (; i < len; ++i)
    {
        if (str[i] == tok)
        {
            str[i] = '\0';
            ++(*parts);
        }
    }
}

char* reverse_sentence(char* str)
{
    char* tmp = str;
    reverse(str);

    int i = 0, parts = 0, len = strlen(str);
    nulltok(str, 0x20, &parts);

    while(parts--)
    {
        reverse(str);
        str += strlen(str) + 1;
    }

    for(; i < len; ++i)
        if (tmp[i] == '\0')
            tmp[i] = 0x20;

    return tmp;
}


int main(void)
{
    char str[] = "dogs like cats";
    printf("%s", reverse_sentence(str));

    return 0;
}

答案 4 :(得分:0)

我的解决方案

#include<iostream>
#include<string.h>
using namespace std;
int main()
{
string str;
cout<<"enter the sentence"<<endl;
getline(cin,str);
char* pch;
pch = strtok((char*)str.c_str()," ");
string rev = "";

while(NULL != pch)
{
    rev.insert(0,pch);
    rev.insert(0," ");
    pch = strtok(NULL," ");
}
cout<<"the reversed string is :"<<rev<<endl;
return 0;

}