字符串系列(洛谷刷题记录)

语言:C语言

P1015 [NOIP1999 普及组] 回文数

题目描述:

若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。

例如:给定一个十进制数 566,将 56 加 65(即把 56 从右向左读),得到 121 是一个回文数。

又如:对于十进制数 87:

STEP1:87+78=165
STEP2:165+561=726
STEP3:726+627=1353
STEP4:1353+3531=4884

在这里的一步是指进行了一次 N 进制的加法,上例最少用了 4 步得到回文数 4884。

写一个程序,给定一个 N(2≤N≤10 或 N=16)进制数 M(100 位之内),求最少经过几步可以得到回文数。如果在 30 步以内(包含 30 步)不可能得到回文数,则输出 Impossible!

题解如下:

#include "cstdio"
#include <string.h>
#include "stdlib.h"

char M[200] = {0};//输入最多100位,为防止进位溢出,开200位的数组
char Mr[200] = {0};//M's reverse倒过来的字符串
int N = 0;//代表N进制
int count = 0;//看进行了多少次加法
int l = strlen(M);

void add();
bool isPalindrome();
int main(){
    scanf("%d %s",&N,&M);
    l = strlen(M);
    for(int i=0;i<l;i++){
        if((M[i]>='0')&&(M[i]<='9')){
            M[i] = M[i] - '0';
        }
        else{
            M[i] = M[i] - 'A' + 10;
        }
    }
    while((!isPalindrome())&&count<=30){
        add();
        count++;
    }
    if(count<=30) printf("STEP=%d",count);
    else printf("Impossible!");
}
void add(){
    //先求出Mr
    for(int i=0;i<l;i++){
        Mr[i] = M[l-1-i];
    }
    Mr[l] = '\0';
    //逐位加
    l++;//防止有进位
    for(int i=0;i<l;i++){//虽然这样是从最高位开始加,但是两个互为逆序的字符串加起来,正着加和反着加是一样的
        M[i] = M[i] + Mr[i];
        if(M[i]>=N){
            M[i] -= N;
            M[i+1]++;
        }
    }
    while(!M[l-1]){
        l--;
    }
}
bool isPalindrome(){
    for(int i=0;i<l;i++){
        if(M[i]!=M[l-1-i]) return false;
    }
    return true;
}

本题需要注意的地方:

1. strlen不能乱用。本题中,我把输入的字符数组的数字ASII码转换成了数字本身。而如果通过strlen求数字数组的长度的话,长度很可能出错,因为strlen是通过字符数组的结束标志'\0'来判断数组长度的,而数字可能没结束就会有0。

2. 在进行N进制加法时,本应该从低位加到高位(而数组下标最小的元素反而是最高位),同时注意满N进位。而此题有一个比较巧妙的地方。就是M和Mr互为逆序。而把它们加起来的话,不管是按正常规则从低位往高位加,还是从高位往低位加,结果都是一样的。所以方便起见,我们直接从高位开始加(也就是从数组下标最小的元素开始)。

3. 注意出循环的条件,一定要自己试一试。像我之前主函数里的while()循环条件就错了,导致就算是Impossible,我还是会输出STEP=30。这就是少循环了一步导致的。

4. 注意结构化的思想,做N进制加法判断是否是回文序列可以抽出两个单独的函数。

P1098 [NOIP2007 提高组] 字符串的展开

题目描述:

在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或者“4-8”的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为“defgh”和“45678"。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:

(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号“-”,减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。

(2) 参数p1​:展开方式。p1​=1时,对于字母子串,填充小写字母;p1​=2时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1​=3时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号“*”来填充。

(3) 参数p2​:填充字符的重复个数。p2​=k表示同一个字符要连续填充k个。例如,当p2​=3时,子串“d-h”应扩展为“deeefffgggh”。减号两边的字符不变。

(4) 参数p3​:是否改为逆序:p3=1表示维持原来顺序,p3​=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当p1​=1、p2​=2、p3​=2时,子串“d-h”应扩展为“dggffeeh”。

(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:“d-e”应输出为“de”,“3-4”应输出为“34”。如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:“d-d”应输出为“d-d”,“3-1”应输出为“3-1”。

 题解如下:

#include "cstdio"
#include "string.h"
bool isSame(char l,char r);
int main(){
    int p1,p2,p3;
    char s[300] = {0};
    char s1[100000] = {0};//s1是扩展后的s,使用双指针来扩展
    int l;
    int count = 0;
    scanf("%d %d %d",&p1,&p2,&p3);
    scanf("%s",s);
    l = strlen(s);
    for(int i=0;i<l;i++){
        if(s[i]!='-') s1[count++] = s[i];//count也需要自增
        else if(!isSame(s[i-1],s[i+1])){
            s1[count++] = s[i];
        }
        else{
            if(i==0){//如果第一个就是’-‘
                s1[count++] = s[i];
                continue;
            }
            else if((s[i-1]=='-')||(s[i+1]=='-')){//有连着的'-'
                s1[count++] = s[i];
                continue;
            }
            int diff = s[i+1] - s[i-1];
            if(diff<=0) s1[count++] = s[i];
            else if(diff == 1){
                s1[count++] = s[i+1];
                i++;//s[]中的指针往后挪一位
            }else{
                char tmp[100000] = {0};
                int index = 0;//暂存展开内容的数组的下标
                if(p1==1){//用小写字母填充
                    for(int j=0;j<diff-1;j++){//输入不会有大写字母
                        for(int k=0;k<p2;k++){
                            tmp[index+k] = s[i-1] + 1 + j;
                        }
                        index += p2;
                    }
                }
                else if(p1==2){//用大写字母填充
                    for(int j=0;j<diff-1;j++){
                        for(int k=0;k<p2;k++){
                            if((s[i-1]>='a')&&(s[i-1]<='z')){
                                s[i-1] = s[i-1] - 'a' + 'A';
                            }
                            tmp[index+k] = s[i-1] + 1 + j;
                        }
                        index += p2;
                    }
                }
                else if(p1==3){//用*填充
                    for(int j=0;j<diff-1;j++){
                        for(int k=0;k<p2;k++){
                            tmp[index+k] = '*';
                        }
                        index += p2;
                    }
                }
                if(p3 == 2){
                    char tmp1[10000] = {0};
                    int l1 = strlen(tmp);
                    for(int i=0;i < l1;i++){
                        tmp1[i] = tmp[l1-1-i];//逆序
                    }
                    for(int i=0;i< strlen(tmp);i++){
                        tmp[i] = tmp1[i];
                    }
                }
                //现在把tmp赋值回给s1
                for(int i=0;i< strlen(tmp);i++){
                    s1[count++] = tmp[i];
                }
            }
        }
    }
    printf("%s",s1);
}

bool isSame(char l,char r){
    bool lt1 = (l>='a')&&(l<='z');//left type 看是否为字母
    bool lt2 = (l>='0')&&(l<='9');//left type 看是否为数字
    bool rt1 = (r>='a')&&(r<='z');
    bool rt2 = (r>='0')&&(r<='9');
    return (lt1&&rt1)||(lt2&&rt2);
}

本题需要注意的地方:

1. 最重要的问题:数组千万不能开小了。血与泪的教训,debug一晚上,跟踪调试发现是数组空间不够导致bug。下次直接开个十万。如果题目有内存限制,就自己先算一下最大大概多大,而不是想当然开一个空间100的数组。(当然C语言需要自己分配、管理内存还是比较麻烦的,建议以后用C++、Java)

2. 注意一些特殊情况,比如‘-’出现在首位、连续的‘-’等情况。

3. 好好读题,‘-’的两边类型不同的情况下,不需要扩展。而且题目说了只会有小写字母、数字和‘-’,不必操心大写字母的事。好好读题能省很多debug的事。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值