HJ31 单词倒排 题解

题目描述:单词倒排_牛客题霸_牛客网 (nowcoder.com)

对字符串中的所有单词进行倒排。

1、构成单词的字符只有26个大写或小写英文字母;

2、非构成单词的字符均视为单词间隔符;

3、要求倒排后的单词间隔符以一个空格表示;如果原字符串中相邻单词间有多个间隔符时,倒排转换后也只允许出现一个空格间隔符;

4、每个单词最长20个字母;

HJ31 单词倒排 题解_第1张图片

 方法一:

定义一个字符指针数组,用于保存每个单词的起始字符地址,接下来将非字母字符全部替换成为字符串结尾标志'\0',则单词字符字母遇到结尾就结束了,相当于把一个字符串以非字母字符进行切割成为了多个字符串,最终对字符指针数组进行逆序打印每个单词即可。
 

  1. 创建一个字符数组来存储输入字符串,并用一个指针指向字符串的开头;创建一个字符指针数组,用于保存每个单词的起始字符地址;
  2. 创建一个循环,遍历输入字符串中的每个字符;
  3. 在循环中,检查当前字符是否为字母(大小写字母),你可以使用C标准库中的isalpha()函数来进行检查:
    1. 如果是字母,标记单词的起始位置,然后移动指针知道跳过这个单词(但指针指向的不是字母字符时,就跳过了一个单词);
    2. 如果遇到非字母字符(即不构成单词的字符),将该字符替换成为字符串结尾标志'\0',向后移动指针;
  4. 下标由大到小地打印字符指针数组的内容。

代码:

#include 
#include 
int main() {
    char arr[10001] = {0};
    char* dst[10001] = {NULL};
    gets(arr);
    char* move = arr;
    int count = 0;
    while(*move != '\0')
    {
        // 当遇到字母字符时
        if(isalpha(*move))
        {
            //记录单词的起始地址
            dst[count++] = move;
            // 跳过这个单词,找到非字母字符
            while(*move != '\0' && isalpha(*move))
            {
                move++;
            }
            continue;
        }
        // 当遇到非字母字符时
        *move = '\0';
        move++;
    }
    // 逆序打印
    for(int i = count-1;i>=0;i--)
    {
        printf("%s ", dst[i]);
    }
    return 0;
}

注:

在跳过一个单词的循环时,如果只是下面这种形式,可能会造成越界访问。例如:字符串为 “I am a student\0”。

while(isalpha(*move))
    move++;

在跳过最后一个单词 student 时,指针会指向 ’\0‘,跳出循环进行下一步:*move = ’\0‘ ;move++,即 ’\0‘ = ’\0‘ ;move++。这时move会指向字符串 ’\0‘ 后面一个位置,然后再一次进行外循环判断条件 *move != '\0',对move进行解引用,此时就发生了越界访问。

所以,正确的写法如下:

if(isalpha(*move))
{
    //记录单词的起始地址
    dst[count++] = move;
    // 跳过这个单词,找到非字母字符
    while(*move != '\0' && isalpha(*move))
    {
        move++;
    }
    continue;
}

这样,在跳过最后一个单词 student 时,指针指向 ’\0‘,跳出循环,执行continue使下一步:*move = ’\0‘ ;move++,不被执行,然后再一次进行循环判断 *move != '\0','\0' != '\0',条件为假,结束循环。

而且添加 continue 不会影响普通情况,例如:当跳过单词 am 时,move指向空格,执行continue,进行外循环判断条件  *move != '\0' , ’ ‘ != '\0',条件为真,进入循环:判断空格是否为字母字符--不是字母字符执行 *move = '\0'; move++; 

方法二:

因为,之前做过 单词逆置 这个题目(整体逆置,再逆置每一个单词),所以我用了这种思路,不过这种方法比较暴力。如果你想要了解 单词逆置 的话,可以看一下http://t.csdn.cn/pwNTt这篇博客。

步骤:

  1. 整体逆置,再逆置每一个单词,此时的字符串只需要再将单词之间的非字母字符(一个或多个)变成空格即可;
  2. 创建一个新的字符数组,用一个指针指向逆置后字符串的开头,进行判断:
    1. 如果指针指向的是字母字符,就将指针指向的内容添加到新的字符数组中;
    2. 如果指针指向的不是字母字符,就将空格添加到新的字符数组中;
  3. 最后打印新的字符数组。

因为单词之间只有一个空格,所以并不能 “ 遇到一个非字母字符时,就将空格添加到新的字符数组中” ;我们发现指针指向的非字母字符是字母字符后面一个非字母字符时,才添加空格,所以这里可以用一个变量来作为添加空格的条件:

当指针指向的是字母字符时,flag = 1;

当指针指向的是非字母字符时,判断条件:如果flag等于1时,添加空格,并将flag=0;如果flag不等于1,就不添加。

所以步骤就变成这样:

  1. 整体逆置,再逆置每一个单词,此时的字符串只需要再将单词之间的非字母字符(一个或多个)变成空格即可;
  2. 创建一个新的字符数组,用一个指针指向逆置后字符串的开头,进行判断:
    1. 如果指针指向的是字母字符,就将指针指向的内容添加到新的字符数组中,flag赋值为1;
    2. 如果指针指向的不是字母字符,如果flag等于1时,就将空格添加到新的字符数组中,如果flag不等于1,就不添加;flag赋值为0;
  3. 最后打印新的字符数组。

代码:

#include 
#include 
#include 
void reverse(char* left, char* right)
{
    while (left < right)
    {
        char temp = *left;
        *left = *right;
        *right = temp;

        left++;
        right--;
    }
}
int main() {
    char arr[10000] = { 0 };
    gets(arr);
    int len = strlen(arr);
    reverse(arr, arr + (len - 1));//逆置整体
    char answer[30] = { 0 };
    int flag = 0;
    char* dst = answer, * move = arr;
    // 逆置单词
    char* start = arr;
    char* end = start;
    while (*start != '\0')//当*start等于\0时,停止逆序
    {
        while (isalpha(*end) && *end != '\0')//注意最后一个单词的后面不是空格
        {
            end++;
        }
        reverse(start, end - 1);
        if (*end != '\0')//如果最后一个单词在向后移动时,会导致start跳过\0从而导致死循环
            end++;
        start = end;
    }
    // 将字符添加到新的字符数组中
    while (*move != '\0')
    {
        if (isalpha(*move))
        {
            *dst = *move;
            dst++;
            flag = 0;
        }
        else
        {
            if (flag == 0)
            {
                *dst = ' ';
                dst++;
                flag = 1;
            }
        }
        move++;
    }
    *dst = '\0';
    puts(answer);
    return 0;
}

今天的分享就到这里了,如果,你感觉这篇博客对你有帮助的话,就点个赞吧!感谢感谢……

你可能感兴趣的:(算法)