1、1第二章 字符串处理和进制转换在程序设计时,如果输入数据一行包含多种信息,以字符串的形式出现,这时就需要用到字符串处理的知识来进行操作。在计算机领域中,有时需要将十进制数转换成二进制、八进制和十六进制的数,有时又需要逆向转换,即将二进制、八进制和十六进制的数转换成十进制的数,或它们相互间进行转换。因此,本章主要介绍字符串处理和进制转换的相关知识。第 1 课 贝贝的交通指挥系统(jqr.pas)【问题描述】贝贝所居住的城市有很多个交通路口,其中有 26 个交通路口在上下班高峰期总是塞车,严重影响市民的出行。于是交通管理部门研制了一批机器人交通警察,用它们来专门指挥这 26 个交通路口,但需要一个
2、自动化的指挥系统来指挥机器人的运作。这个任务交给了贝贝,贝贝的设计如下。分别用大写英文字母 A、B、Z 表示这 26 个路口,并按如下的规则派出这些机器人到交通路口协助指挥交通:1每次派出两名机器人; 2当两名机器人的名字中存在一个相同的字母时,这两名机器人便到对应的交通路口上指挥交通;有多个字母相同时,两名机器人需要按字母的字典顺序到这些路口上巡逻;3当两名机器人的名字中不存在相同的字母时,交警部门的派出指令无效( WuXiao)。假设这些机器人的名字全由大写字母组成,请你编一个程序,帮贝贝完成这个交通指挥系统。【输入格式】jqr.in第 1 行输入第一个机器人的名字(长度不超过 250)
3、;第 2 行输入第二个机器人的名字(长度不超过 250) 。【输出格式】jqr.out1当不能派出机器人时,在第一行输出“WuXiao” ;2当两名机器人在路口上指挥交通时,在第一行输出“ZhiHui” ,第二行输出路口编号;3当两名机器人在路口上巡逻时,在第一行输出“XLuo“,第二行输出巡逻的路口数,第三行输出巡逻线路。【输入输出样例】输入 输出样例 1 OPENCLOSEXLuo2E-O样例 2 EPSONSENPUMXLuo4E-N-P-S分析问题题目意思:输入两个机器人的名字,统计他们的名字中出现相同字母的个数,由相同字母的个数决定他们巡逻的路线。当两名机器人的名字中存在一个相同的字
4、母时,这两名2机器人便到对应的交通路口上指挥交通;有多个相同的字母时,两名机器人需要按字母的字典顺序到这些路口上巡逻;当两名机器人的名字中不存在相同的字母时,交警部门的派出指令无效。解决问题1方法一因为输入的机器人名长度不超过 250,可用字符串方式读入数据。用两个字符串保存数据,使用双重循环,用枚举法查找相同的字母,用一个数组保存这些相同的字母,并用一个变量 total 记录相同字母的个数,然后按字母顺序排序,最后根据相同字母的个数分下面三种情况输出结果:如果 total=0,则输出“WuXiao“;如果 total=1,则输出“ZhiHui“;如果 total1,则输出“XLuo“。最后按
5、题目要求输出巡逻路线。 参考程序如下:上述程序未考虑题目中隐含的条件,即机器人名字中可能会出现重复的相同字母。上面处双重循环的代码,由于忽略题目隐含信息,把重复字母也重复计算在 total 上,最终 total 的结果是错误的,导致输出路线的错误。应怎么解决呢?我们可以设置一个数组标识出现过的相同字母,实现如下:for i:=A to Z do bi:=true;for i:=1 to length(str1) dofor j:=1 to length(str2) doif (str1il=str2j)and bstr1i thenbegin inc(total) ; atotal:=strl
6、i; bstr1i: =false; end;算法效率:程序中统计相同字母个数和使用简单排序对字母进行排序时,用了双重循环,因此时间复杂度为 O(n2),而 n 的值为机器人名字长度,最长为 250 个字符,即var str1,str2:string;total,i,j,temp:longint;a:array01000 of integer;beginreadln(str1);readln(str2);/读入两个机器人的名字total:=0;/记录相同字母个数,清零for i:=1 to length(str1) do /for j:=1 to length(str2) doif str1i
7、=str2j then begin inc(total);atotal:=str1i;end;if total=0 then writeln(WuXiao);if total=1 then begin writeln(ZhiHui);writeln(atotal);end;for i:=1 to total-1 dofor j:=i+1 to total doif aiaj then begin temp:=ai;ai:=aj;aj:=temp;end;if total1 then beginwrite(a1);for i:=2 to total do write(-,ai)end;end.3
8、n2=250250=62500 1 then /如果 total1,则输出“XLuo”begin writeln(XLuo);writeln(total);end;if total=1 then writeln(ZhiHui);if total=0 then writeln(WuXiao);total:=0;/计数器清零for j:=A to Z do /按字母顺序输出巡逻路线if (aj) and (bj) thenbegininc(total);if total=1 then write(j) /只有一个路口,则输出相应的字母else write(-,j);end;end.4活学活用1贝贝
9、的车牌问题(car)【问题描述】广州市车管所为每一辆入户的汽车都发放一块车牌,车牌的号码由六个字符组成,如A99452、B88888 等,这个字符串从左边数起的第一个字符为大写英文字母,如 A、B、C 等,表示这辆车是属于广州市区内的汽车还是郊区的汽车,后面的五位由数字组成。假定以字母 A、B、C、D、E、F、G、R、S、T 开头的表示是市区车牌,而以其他字母开头的表示郊区车牌。车管所把这个任务交给贝贝。请你帮贝贝找出所给出的车牌中有多少辆是广州郊区的汽车。【输入格式】第 1 行是一个正整数 N(1N10 5),表示共有 N 个车牌。接下来的 N 行,每行是一个车牌号。题目保证给出的车牌不会重
10、复。【输出格式】只有 1 行,即广州郊区车牌的数量。【输入样例】3G54672Q87680P77771【输出样例】22贝贝的 ISBN 号码(isbn)【问题描述】每一本书都有一个 ISBN 号码,包括 9 位数字、1 位识别码和 3 位分隔符,其规定格式如“X-XXX-XXXXX-X” ,其中符号“-”是分隔符(减号) ,最后一位是识别码,例如 0-670-82162-4 就是一个标准的 ISBN 码。ISBN 码的首位数字表示书籍的出版语言,例如 O 代表英语;第一个分隔符“-”之后的三位数字代表出版社,例如 670 代表出版社;第二个分隔符之后的五位数字代表该书在出版社的编号;最后一位为
11、识别码。出版社想开发一套自动化识别系统,判断输入的 ISBN 号码中识别码是否正确,如果正确,则仅输出“Right” ;如果错误,则输出你认为是正确的 ISBN 号码。于是,出版社把这个任务交给了贝贝。识别码的计算方法如下:首位数字乘以 1 加上次位数字乘以 2以此类推,用所得的结果 mod 11,所得的余数即为识别码,如果余数为 10,则识别码为大写字母 X。例如ISBN 号码 0-670-82162-4 中的识别码 4 是这样得到的:对 067082162 这 9 个数字,从左至右,分别乘以 1,2,9,再求和,即 O1+62+29=158,然后取 158 mod 11 的结果 4 作为识
12、别码。请你帮贝贝完成这个任务。【输入格式】只有 1 行,是一个字符序列,表示一本书的 ISBN 号码(保证输入符合 ISBN 号码的格式要求) 。【输出格式】假如 ISBN 识别码正确,那么输出“Right” ,否则输出正确的 ISBN 号码(包括分隔符“一” ) 。【输入输出样例】5输入 输出样例 1 0-670-82162-4 Right样例 2 0-670-82162-0 0-670-82162-4第 2 课 贝贝的图形(vhist)【问题描述】贝贝最近玩起了字符游戏,规则是这样的:读入四行字符串,其中的字母都是大写的,乐乐想打印一个柱状图显示每个大写字母的频率。你能帮助他吗?【输入格式
13、】输入文件共有 4 行:每行为一串字符,不超过 72 个字符。【输出格式】与样例的格式保持严格一致。【输入样例】THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG.THIS IS AN EXAMPLE TO TEST FOR YOURHISTOGRAM PROGRAMHELLO! 【输出样例】说明:1输出的相邻字符间有一个空格。2最后一行的 26 个大写字母每次必须输出。3大写字母 A 所在的第一列前没有空格。分析问题题目意思:输入四行字符串,长度都不超过 72,统计大写字母的个数,输出结果时,用“*”表示字母出现的频率,并按样例说明的要求输出。解决此问题
14、的难点在于输出格式的控制,细看样例可知,输出的内容就只有空格、星号和大写字母,除最后一行为大写字母外,上面输出的每行都有空格和星号,那么,问题就成了如何控制空格和星号的输出。只要解决此问题,本题就解决了。通过问题分析,我们应怎么样控制空格和星号的输出呢?我们先把题目的输出样例转化成如下表:表 2.2-1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15、* * * * * * * * * * *A B C D E F G H I J K L M N O P Q R S T U V W X Y Z图 2.2-16字母 A B C D E F G H I J K L M N 0 P Q R S T U V W X Y Z星号个数 5 l 1 2 8 2 3 5 4 1 1 4 4 2 10 3 1 7 4 7 3 1 1 2 2 1由表中的数据可以知,出现频率最多的是字母“O” ,为 10 次,也就是说,这个数值决定了你输出的图形有 1 0 行,并都是由空格和星号组成,我们就倒过来为这些图形定义一个行号,从最上面第一行开始,行号为 1 0、9、8
16、10 如下面图 2.2-2 所示。图 2.2-2行数定了,我们再看看,如何控制每行空格和星号的输出。我们就取其中一个字母“E” ,它首先打印的“*”所在的行行号为 8,它出现的频率是 8,也就是说,在第 E 列上,从第1 0 行到第 9 行每行都有一个空格,从第 8 行到第 1 行每行都有一个“*” ,显然,当行数小于或等于当前字母的频率时,打印“*” ,否则打印空格。因此,我们设一个以 26 个大写字母为下标的数组来记录每个字母出现的频率,同时,找出频率最高的字母和频率值(决定图形的最大行数) 。代码如下:max:=O: /记录频率最大值for i:=1 to 4 dobeginreadln
17、 (s):for j:=l to length (s)do /统计频率最高的字母和频率值if(sj=A)and(sjmax then max:=asj;end;end;输出的图形是矩阵形的,因此根据前面的分析用一个双重循环,输出空格和“*” ,外循环控制行数变化,内循环根据当前字母的频率打印空格或“*” 。代码如下:for i:=max downto 1 do beginfor h:=A to Z do /检测每一列是否打印空格和“*”/由题目要求输出的相邻字符间有一个空格,所以后面多一个空格if ah=ithen write(*) else write( ):writeln;end;10行
18、*9行8行 * *7行 *6行 * *5行 * * *4行 * *3行 * * *2行 * * *1行 * * *ABCDEFGHIJKLMNOPQRSTUVWXYA7参考程序如下:算法效率:上面模块一的程序段是双重循环,外循环的运行次数为 4 次,内循环运行次数最多为 72(所读入字符串的长度) ,总的运行次数为 472=288,显然不会超时。模块二的程序段也是双重循环,外循环的运行次数为 max,当输入的字符中所有的字母都是同一个字母时,max 的最大值为 472=288,内循环的运行次数为 26,总运行次数为26288=76608=A)and(sjmax then max:=asj; e
19、nd;end;for i:=max downto 1 do /模块二beginfor h:=A to Z doif ah=i then write(* ) else write( );writeln;end;for h:=A to Z do write(h, );end.8end;readln;end;注意:程序中使用 eoln 或 eof,则调试时必须使用文件输入,不能在屏幕输入调试。活学活用1手机(mobile)【问题描述】手机的键盘是这样的:表 2.2-21 2 abc 3 def4 ghi 5 jkl 6 mno7 pqrs 8 tuv 9 wxyz* O #要按出英文字母就必须要按数
20、字键多下。例如要按出 x 就得按 9 两下,第一下会出w,而第二下会把 w 变成 x。按 O 键一下会出一个空格。你的任务是读取一句只包含英文小写字母和空格的句子,求出要在手机上打出这个句子至少需要按多少下键盘。【输入格式】1 行,一个句子,只包含英文小写字母和空格,且不超过 200 个字符。【输出格式】一个整数,表示按键盘的总次数。【输入样例】i have a dream【输出样例】232不明飞行物(ufo)【问题描述】一颗彗星的后面有一个不明飞行物(UFO),这个 UFO 经常到地球上来寻找忠实的追随者,把他们带到宇宙中去。但由于舱内空间有限,它们每一趟只能带一组追随者。尽管如此,外星人仍
21、然想出了一个妙法来决定带谁走:以 A 代表 1,B 代表 2Z 代表 26,USACO 即211 91315=17955,倘若此组人的组名所代表的数字与彗星的名字所代表的数字分别除以 47,余数相同,则彗星名与组名相匹配,UFO 带此组人飞向宇宙,余数不同则不匹配,故不带。编程任务:写一程序,打印出彗星名与组名是否相匹配,是打印“GO” ,否打印“STAY”;同时打印出两者的余数。【输入格式】输入文件包含两行,第 1 行为慧星名,第 2 行为组名(长度不超过 250) 。【输出格式】输出文件包含两行,第 1 行为是否匹配的信息,第 2 行显示两者的余数。【输入输出样例】输入 输出样例1 COM
22、ETHALEBOPPHEAVENSGATESHOEMAKERLEVYUSACO9样例2 GOrl=r2=17STAYr1=21 r2=1第 3 课 贝贝的加密工作(password)【问题描述】贝贝找了一份为一些文件的某些部分加密的工作,加密的部分是一串小写英文字母,加密的规则是这样的:要是连续出现相同的字母,则把他们替换成这个字母的大写形式,后面紧跟相同字母的个数,并把它之前跟之后的两段字串调换,例如出现 bcaaaaaaef,则新字符串变成:efA6bc,然后重新扫描字串,直到没有出现相同小写字母为止。【输入格式】原始字符串(长度不大于 250) 。【输出格式】新字符串。【输入输出样例】输
23、入 输出样例1 bcaaaaaaef efA6bc样例2 cmmmcefffg gM3cF3ce分析问题结合上面的输入输出样例,很容易理解题目的意思,解决问题的难点在于:当检测出字符串里连续出现的字母及其个数时,应该怎么样把前后的两段字串调换,同时再判断新的字串里有没有连续重复的字母,如有则重复操作,否则输出结果。解决问题结合上面的问题分析,我们通过以下几个步骤来解决此问题。1读入字串由于字串长度不大于 250,可用字符串变量 s 来保存读入的字串。2检测首先出现连续重复字母的起始位置定义一个指针 i,指向字串的第一个字母,比较前后字母来移动指针。代码如下:while (sisi+1) and
24、 (i0 do /把求余的结果保存在数组 a 中begininc(i);ai:=da mod k;da:=da div k;end;for j:=i downto 1 dosum:=sum+ai; /各位数字求和st:=sum;end;由于题目只要求转换后的各位数字之和,并没有要求输出转换后的进制数。为了简化程序,可以对前面的函数进行优化,在求余的过程中对各位数字进行求和。改进如下:function st(da,k:longint):longintvar sum:longint;beginsum:=O;while da10 则用大写字母 AF 表示数码 10 -15,并且该 n 进制数对应的十
25、进制数的值不超过 2000000000,第 3 行也是一个正整数,表示转换之后的数的进制 m(2m16)。【输出格式】1 行,包含一个正整数,表示转换之后的 m 进制数。【输入样例】16FF2【输出样例】11111111分析问题题目意思很明确,就是把输入的任意进制数转化成指定的任意进制数。上一课我们已经学习了十进制数转化成任意进制数的方法,现在问题就是:如何把任意进制数转化成十进制数呢?我们可以先把输入的任意进制数转换成十进制数,再将此十进制数转换成指定的任意进制数。学习新知任意进制整数转换成十进制数,按权展开。设任意进制数为 x,按权展开如下:(1101)2=123+122+021+120,
26、(165)8=182+681+580。把 x 的每一位放在数组 a 中,代码如下:y:=1;s:=ak / Y 是当前位的权值for i:=k-1 downto 1 dobeginy:=y*n;s:=s+ai*y;end;解决问题17在转换过程中要注意以下两个问题:1题目中说到 n 进制数对应的十进制数的值不超过 2000000000,也就是说读入的二进制数可能超过长整型数据类型的范围,并且,读入的数据中也可能出现字母,所以,读入数据时,不能以长整型数据类型读入,只能以字符串的方式读入。2当输入的 n 和 m 都10 时,注意考虑数字 1015 对应大写字母 AF 的表示法。以上两点,很多初学
27、者在编程过程中比较容易忽略,由于没审清楚题目要求,出现编错或者编漏程序,拿不到满分,甚至零分。参考程序如下:program change;var n,m,y,i,j,k,s:longint;x:string;str2:char;a:array150 of longint;function nchange(st:string):longint;vark,s,y:longint;begink:=length(st);for i:=1 to k do /模块一case sti of1,2,3,4,5,6,7,8,9,0:ai:=ord(sti)-ord(0);A,B,C,D,E,F:ai:=ord(
28、sti)-55;end;y:=1;s:=ak;for i:=k-1 downto 1 dobeginy:=y*n;s:=s+ai*y;end;nchange:=s;end;procedure mchange(s,m:longint);vari,j:longint;begini:=0;repeati:=i+1;ai:=s mod m;s:=s div m;until s=0;for j:=i downto 1 do/模块二beginif ajO)and(iO 这一条件。主要程序段如下:t:=O;for m:=1 to 11 dofor k:=1 to 25 dobeginn:=100-9*m-k
29、;if (n0) and (2*m+2*n+4*k=100) thenbegin writeln (m, ,n, ,k); t:=t+1;end;end;writeln (t);4方法四进一步的优化将式2 再减去,这样就消去 N,得到 16M-2K=100,整理可得:27K=8M-50这样就可以将两重循环进一步简化成一重循环。但是,式由 M 计算出来的 K 也可能会出现负数,这是不允许的,故在循环体中要加上 KO 的条件。表达式应写在表达式的前面,即必须先求出 K,才能求 N。主要程序段如下:t:=0;for m:=1 to 11 dobegink:=8*m-50;n:=100-9*m-k;i
30、f (kO) and (nO) thenbegin writeln (m, ,n, ,k);t:=t+1;end;end;writeln (t);上面的四种方法都能给出正确的结果,但所需循环次数分别是100100100、115025、1125、11 次,通过一系列优化措施,以及将不必要的循环去掉,使得程序的效率不断地提高。提高枚举法效率的方法有:(1)在步长值不变的情况下,减少循环变量的初值和终值的差距;(2)在初值、终值不变的情况下,增大步长值;(3)减少循环嵌套的层数。具体使用时,应根据题目的条件,灵活运用。活学活用1桐桐去购物(ji)【问题描述】桐桐周末陪妈妈到市场购物。她和妈妈来到一个
31、买鸡的摊位,发现鸡的价格有三种:公鸡每只 5 元钱,母鸡每只 3 元钱,小鸡 3 只 1 元钱。妈妈就给桐桐出了一道计算题:如果用 n 元钱买 m 只鸡,问公鸡、母鸡和小鸡可以各买多少只?注意:必须把 n 元钱正好用完,且买的各种鸡的只数为大于等于 O 的整数。桐桐回到家里便拿起笔来认真计算,算了好久还没得出答案。聪明的你通过编写程序帮助桐桐找出结果好吗?【输入格式】只有 l 行,两个数 n 和 m(O1 dobeginwhile n1 mod i =0 do /如果能整除 i 则不断整除beginj:=j+1;if j=1 then write(n,=,i) /第一次输出else write(*,i);n1:=n1 div i;end;i:=i+1; /取下一个数end;end.