考试周前最后一次总结,主要关于字符串类问题

一些特殊的字符串类型问题

我们现在已经结束了第一学期的学习,不知道大家收获如何,但是希望大家不要止步于考试能过的程度,而是深入一些,理解透一些,为未来的学习打下坚实的基础。

我们最后学习了数组以及字符串数组,一般类型的问题以及足以 解决,但是有一些典型问题还是需要去深入讨论一下,那么我们以期末的实验测试题为例,相信大家还有印象。

(题目摘自PTA)

7-8 到底有多二(15 分)

一个整数“犯二的程度”定义为该数字中包含2的个数与其位数的比值。如果这个数是负数,则程度增加0.5倍;如果还是个偶数,则再增加1倍。例如数字-13142223336是个11位数,其中有3个2,并且是负数,也是偶数,则它的犯二程度计算为:3/11×1.5×2×100%,约为81.82%。本题就请你计算一个给定整数到底有多二。

输入格式:

输入第一行给出一个不超过50位的整数N。

输出格式:

在一行中输出N犯二的程度,保留小数点后两位。

输入样例:

-13142223336

输出样例:

81.82%

那么我们先对题目进行分析,第一句 ,主要为(包含‘2’的个数),那么显然是 一个需要用到数组遍历的环节,其次“负数”,这里是一个特殊测试点,但我们先不进行处理,“偶数”,同上,我们先略过,之后我们观察样例易得,“二”的程度首先取决于‘2’的个数,这是该问题的一般性,其余的均为特殊性,先不做处理,之后我们观察输入格式 ,重点来了,如果是一般的数据处理显然该题目是容易完成的,但是其中的关键问题在于“50位”,显然,该题目涉及到高位数的处理,要知道,在C语言中,unsigned long long 型的范围也只能到20位数,这里显然是提示我们,需要用到字符串数组 ,那么其余的问题就简单了,下面给出代码:

#include<stdio.h>
#include<string.h>
int main(void)
{
    char ch[100] = {'\0'};     //定义数组存储所给数
    int count = 0,i = 0;      //定义循环变量以及计数器
    double pow = 0.0;        //定义double型存储乘积,即“犯二”程度
    gets(ch);               //输入数据     
    for(i = 0;i < strlen(ch);i++) //首先从一般性入手,查找‘2’的个数
    {
	    if(ch[i] == '2')
	    {
		    count++;
	    }
    }
    if(ch[0] == '-')        //接着,由于负号影响所给数的实际位数判断,因此首先分析该特殊情况
    {
	    pow = count / (double)(strlen(ch) - 1);   //若为负,则总位数减1
	    pow *= 1.5;
    }
    else
    {
	    pow = count / (double)strlen(ch);   //若无特殊情况,则直接计算
    }
    if((ch[strlen(ch) - 1] - '0') % 2 == 0)     //由于判断某数是否为偶只需判断个位,那么我们只需判断最后一位即可
    {
	    pow *= 2;
    }
    pow *= 100;                   //转换为百分制的格式
    printf("%.2f%%",pow);        //输出
    return 0;
}

至此,该题目解决。

下面给出一些小总结。字符串数组常用于高位数据的处理问题,由于已给出的几种类型可能造成的内存溢出问题,因此我们对于高位的数据只能由数组保存,以字符串数组为方法解决的问题的一个显著特征就是高位,所以在拿到题前一定要先审清题目类型,可以节约宝贵的时间。

下面给出字符串数组的另外一种用法,即高位数的运算问题。

上面提到过,高位数我们可以依靠字符串数组来存储,那么运算可以实现吗,实际上是可以的,请回忆一下小学时期是如何进行四则运算的,即末位对齐,分别计算,那么同理,我们只需将两个高位数的末位对齐,再进行计算,最后进位即可,下面给出代码:

#include<stdio.h>
#include<string.h>
int main(void)
{
    char num1[100] = {'\0'},num2[100] = {'\0'},t = '\0';  //设置两个字符串型数组存放待计算的数
    int i = 0,j = 0,l1 = 0,l2 = 0;    //设置两个变量来存放两数的位数  ,以及循环变量
    gets(num1);     //输入数1
    gets(num2);     //输入数2
    l1 = strlen(num1);          //计算两数位数
    l2 = strlen(num2);
    j = l1 - 1;                 //将最后一位对齐
    for(i = l2 - 1;i >= 0;i--,j--)         //开始将两数由末位相加,这里笔者将数2加到数1中
    {
        t = num2[i] - '0';
        num1[j] += t;
    }
    for(i = l1 - 1;i > 0;i--)      //上面加和完成后,开始遍历数组,查找所有比9大的值,并逆向进位
    {
        if(num1[i] - '0' > 9)
        {
	        num1[i - 1]++;
	        num1[i] -= 10;
        }
    }
    if(num1[0] > '9')                //最后判断首位是否需要进位 
    {
	    t = num1[0];
	    num1[0] = '0';
	    for(i = l1 - 1;i >= 0;i--)   //若需要,则将所有元素后移 
	    {
		    num1[i + 1] = num1[i];
	    }
	    num1[1] = t - 10;
	    num1[0]++;
    }
    puts(num1);      //输出
    return 0;
}

这里笔者只给出加法运算,减法以及乘法同理,只要遵循相应的运算法则即可,除法由于涉及到浮点数,因此先不考虑。
其次,笔者在此所给的代码并不严谨,实际上数1的位数是要严格大于数1的,实际上要改进也并不困难,只需要在初始时判断两数长度即可,在此笔者仅想展示该种方法。

由于期末考试临近,篇幅有限,最后再给出一个经典算法,用以快速求最大公约数(GCD),该方法由欧几里得发明,因而被称为欧几里得算法。

对于常规算法,我们判断最大公约数是采取枚举法,即从1~较小数为止,寻找满足能被两数同时取模为0的数,最坏情况下,可能要取到较小数为止,加上期间的取模运算,实际上是相当耗费时间的,当然我们可以用到欧几里得算法,该算法的时间复杂度为O(logB)下面给出函数:

int GCD(int A,int B)              //传入两个正整数A,B 
{
    int t = 1;                    //设置中间变量 
    while(t)
    {
	    t = A % B;                //记录A 与 B第n次取模的结果 
	    A = B;                    //将A B与t的值进行迭代 
	    B = t;
    }
    return A;                     //当A B取模为零时,算法结束 ,返回最后一次的结果
}

数学上的证明由于时间有限在此不做解释了...最后祝大家考试顺利。

posted on 2018-01-06 20:05  ShizukaRi  阅读(242)  评论(0编辑  收藏  举报

导航