一些特殊的字符串类型问题
我们现在已经结束了第一学期的学习,不知道大家收获如何,但是希望大家不要止步于考试能过的程度,而是深入一些,理解透一些,为未来的学习打下坚实的基础。
我们最后学习了数组以及字符串数组,一般类型的问题以及足以 解决,但是有一些典型问题还是需要去深入讨论一下,那么我们以期末的实验测试题为例,相信大家还有印象。
(题目摘自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] = {' '}; //定义数组存储所给数
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] = {' '},num2[100] = {' '},t = ' '; //设置两个字符串型数组存放待计算的数
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取模为零时,算法结束 ,返回最后一次的结果
}
数学上的证明由于时间有限在此不做解释了...最后祝大家考试顺利。