数据结构作业整理:扩展字符

题目展示:

【问题描述】
从键盘输入包含扩展符'-'的字符串,将其扩展为等价的完整字符,例如将a-d扩展为abcd,并输出扩展后的字符串。

要求:只处理[a-z]、[A-Z]、[0-9]范围内的字符扩展,即只有当扩展符前后的字符同时是小写字母、大写字母或数字,并且扩展符后的字符大于扩展符前的字符时才进行扩展,其它情况不进行扩展,原样输出。例如:a-R、D-e、0-b、4-B等字符串都不进行扩展。


【输入形式】
从键盘输入包含扩展符的字符串
【输出形式】
输出扩展后的字符串

【输入样例1】
ADEa-g-m02
【输出样例1】
ADEabcdefghijklm02

【输入样例2】
cdeT-bcd
【输出样例2】
cdeT-bcd

【样例说明】
将样例1的输入ADEa-g-m02扩展为:ADEabcdefghijklm02;样例2的输入cdeT-bcd中,扩展符前的字符为大写字母,扩展符后的字符为小写字母,不在同一范围内,所以不进行扩展。 

思路分析:

首先我们明确一下这道题的目的,即:如果出现“-”且前后均为同类型的字符(整数,大写小写字母),并且满足扩展的顺序(前面的小于后面的),则对其进行扩展,将“-”替换为前后两个字符中间的字符。

所以我们要处理以下的问题:

1.如何判断前后字符是否符合条件

2.如何进行替换,选择什么样的数据结构来进行实现?是否只需要对原来的字符串进行操作?还是需要额外再申请空间?

切入点是:把“-”替换成为中间的字符,那么,首先想到的是读到“-”的时候就去把相应的位置替换。但是原来的字符串没有办法扩展空间(当然如果是使用的动态内存申请另说),而且就算使用的是动态内存申请,那么每添加一个字符就会需要把后面所有的字符全部向后移动一位,因此耗费时间很大,并不划算。(这里没有使用链表,当然,如果使用链表的话会非常简单)

在不使用链表的情况下,我们只能选择数组来进行数据的存储。这里我来谈谈第一个很重要的思想:删除/替换,不一定非要盯着原来的结构不放,不妨换个思路,使用另外一个数组来把符合条件的元素留下,再把需要进行操作的元素进行相应的操作后再放进新的数组里面,这样就会节省很多的时间

因此,我们使用新数组newstr来存储改变后的字符串。具体的操作如下:

1.从头开始遍历,当元素不是要被替换的元素时,直接把他存储到新数组中。

2.当遇到“-”的时候,把前面一个字符记为begin,后面一个字符记为end,然后对这两个字符进行是否符合条件的判断,如果符合,那么进行一个while循环,把从begin到end的所有字符放入新数组中,然后原数组继续前进;如果不符合条件,直接原数组前进,跳过“-”。

3.最后在新数组后面加上“\0”,代表他是一个字符串。

其实整个过程还是十分清晰的,关键点有几个:

1.使用新数组进行存储,让时间复杂度为O(n).

2.最后要给新的字符串加上‘\0’让输出能够正常输出。

小小的延申:

针对这道题,其实还有一个很相像的操作,即删除字符串中的指定字符,这个由于我们一种思路是可以使用新数组来存储,但是需要花费额外的空间;还有一种的可行思路是:只针对字符串的本身进行操作,使用类似双指针的方法,设置快慢指针(即快慢下标),如果是正常的字符,快慢下标同时向前移动,快指针给满指针赋值;当遇到要被删除的指针时,快指针还是正常向前,但是慢指针则停在原处,此时快指针已经跳过了待删除的字符,直接将下一位字符赋值给仍然处在待删除位置的慢指针,将原来的内容覆盖掉了,巧妙地实现了覆盖。

那么如何实现快与慢呢?快指针由于是全过程都需要遍历的,因此我们可以把它放在循环的条件中,不受条件的约束;而慢指针则需要用if条件来判断,他的向前是依靠于是否有条件出现的。在条件中赋值时把慢指针的前移用j++放在数组下标中实现,i++则在循环的条件中,当遇到指定字符时,不执行慢指针向前并赋值的操作,实现跳过。

void squeez(char s[],char c)
{
 int i,j;
 for(i=j=0;s[i]!='\0';i++)//i是正常移动的
  if(s[i]!=c)
   s[j++]=s[i];//如果等于c时,则不执行j++操作,意味着慢指针不移动
 s[j]='\0';
}

            

代码实现:

#include <stdio.h>  
#include <ctype.h>  
#include <string.h>  
#include <stdlib.h>  
  
int main() {  
    char s[1000];  
    char *newstr;  
    int begin, end;  
    gets(s); 
    int len = strlen(s);  
    newstr = (char *)malloc(sizeof(char)*(len+1000));
   int j = 0;  
    for (int i = 0; i < len; i++) {  
        if (s[i] == '-') {//这个if是如果有“-”相应操作 
            if ((i > 0 && i < len - 1) &&((isupper(s[i - 1]) && isupper(s[i + 1]) )||(isdigit(s[i - 1]) && isdigit(s[i + 1]))||(islower(s[i-1])&&islower(s[i+1])))) //条件判断1,是否符合类型一致
            {   
                begin = s[i - 1];  
                end = s[i + 1];  
                if ((isupper(begin) && isupper(end) && end > begin) ||(isdigit(begin) && isdigit(end) && end > begin)||(islower(s[i-1])&&islower(s[i+1])))//条件判断2,是否顺序正确
                {  
                    for (char c = begin+1; c <= end; c++) 
                    {  
                        newstr[j++] = c;  //把缺失的补全
                    }  
                    i++; //原数组向前移一位,进行下一位的判断
                    continue;  
                }  
            }  
        } 
        newstr[j++] = s[i]; //正常的字符直接读入即可
    }  
    newstr[j] = '\0';
    printf("%s\n", newstr);  
    free(newstr);
    return 0;  
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值