关于C语言作业中 E-排列问题 的字符输入
提前声明,本题正解应该是字符串读入,在下仅对单个字符读入方式进行分析
文章精髓在于原理部分的引用,若有兴趣,请详读。若嫌长,也有找bug的过程和结论,还有最后的AC代码供参考。
在?找找bug?
本题的题意特别单纯,T组样例,读入一组样例中两行字符串,判断两行字符串能不能相互转化,能就输出Yes,不能就输出No。于是主函数代码框架就可构建如下
scanf("%d", &T); //读入T
while(T--)
{
Input(); //读入字符串
check(); //判断能不能相互转化,并输出
}
判断很简单,统计每一串字符串中每个数字出现的次数,开两个数组a[10]和b[10],输入一个字符ch便在a[ch-‘0’]++,最后在check时从1到9开始遍历,若a[i]!=b[i],则输出No并结束函数,遍历后没有结束check函数就输出Yes
思路很单纯,但是这个题做起来没有想象中那么顺利,关键在于输入字符串的Input这个过程,我先贴一段我在本地编译器AC的输入函数
void Input()
{
while(scanf("%c", &ch)==1) //当有字符输入时,继续循环
{
if(ch<'0' || ch>'9') break; //一旦有除数字以外的字符就打破循环,一般来说这个字符会是换行符或者空格
a[ch-'0']++;
}
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
b[ch-'0']++;
}
}
而同样的一段代码在oj上显示的却是wrong answer,如果你打开一个网络编译器(这个上网一搜能找到一堆,我用的是洛谷的IDE),在洛谷的评测机上跑出来也不是想要的答案,经过尝试和修改,正确输入如下
void Input()
{
ch = getchar(); //在输入字符串前读入一个字符
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
a[ch-'0']++;
}
ch = getchar();
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
b[ch-'0']++;
}
}
同样的,我在读入T后进入循环之前也读入了一次ch=getchar();
为什么要多读入一个字符呢??我们来看看在循环中读入这个字符之前和之后是这个全局变量ch是什么吧
void Input()
{
printf("%d\n", ch); //插入打印ch的ASCII,换行
ch = getchar();
printf("%d\n", ch); //再打印此时的ASCII,换行
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
a[ch-'0']++;
}
ch = getchar();
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
b[ch-'0']++;
}
}
最终得到的结果如图
我们知道二者都不是数字对应的ASCII,在网上查一下ASCII对照表,就知道13对应的是CR(回车键),10对应OA(换行键),而在本地试验ch却没有出现过ch的ASCII为13的情况。
我们可以猜测,网络评测机会多录入一个回车符,这就是网络评测和本地评测的不同。
在?查查原理?
后来问过老师才知道,这是操作系统的问题,在linux和Windows上换行的表示是不一样的,一个是\n,一个是\r\n。并且注意,当输入的字符是\r时,scanf认为是有效输入的(亲测确认),所以需要多读入一次ch。
至于原理,这里有一篇博客讲的很到位
先来段历史
回车”(Carriage Return)和“换行”(Line Feed)这两个概念的来历和区别。
符号 ASCII码 意义
\n 10 换行
\r 13 回车
CR 在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model
33,Linux/Unix下的tty概念也来自于此)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。
后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。在Windows中: ‘\r’ 回车,回到当前行的行首,而不会换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖; ‘\n’
换行,换到当前位置的下一行,而不会回到行首;Unix系统里,每行结尾只有“<换行>”,即"\n";Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;Mac系统里,每行结尾是“<回车>”,即"\r";。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
分别在Windws和Linux中查看此文件可知:
Linux中遇到换行符("\n")会进行回车+换行的操作,回车符反而只会作为控制字符("^M")显示,不发生回车的操作。而windows中要回车符+换行符("\r\n")才会回车+换行,缺少一个控制符或者顺序不对都不能正确的另起一行。
c语言编程时(windows系统)\r 就是return 回到 本行 行首 这就会把这一行以前的输出 覆盖掉 如: int main() {
cout << “hahaha” << “\r” << “xixi” ; } 最后只显示 xixi 而 hahaha 被覆盖了 \n
是回车+换行 把光标 先移到 行首 然后换到下一行 也就是 下一行的行首拉 int main() { cout << “hahaha” <<
“\n” << “xixi” ; } 则 显示 hahaha xixi
在windows 系统中,当你输入回车时会自动变成\r\n在linux下的回车键只代表\n
而在windows下的回车键表示\r\n
\n为进入下一行,\r为打印头回到行首上
linux/unix下只用\n,它就表示回车+换行
而windows下,\r只回车不换行的,\n是换行,但在有些编辑中,单独的\n是不会换行的(如notepad)一般在程序中,写\n就可以了,它在linux或windows中都能实现回车+换行的功能(只是在文本文件中,linux只会有0x0a,windows会自动换为0x0d
0x0a)
正是因为这样的不同导致了我们代码在OJ上出现wrong anser,改正方案如上,多读入一次ch即可在网络评测时避免CR的影响,但是同样,在本地编译的话则不会得到想要的结果。 所以说一个备用的在线编译网站是多么重要 不过这样的差异对一般问题没有太大影响,只有当处理字符串时需要注意
在?看看代码?
最后AC代码如下,不同的人有不同的录入思路和判断思路,在下的垃圾代码仅供参考
#include<stdio.h>
#include<string.h>
#define maxn 12
int T, a[maxn], b[maxn];
char ch;
void check()
{
for(int i=1; i<=9; i++)
if(a[i]!=b[i])
{
printf("No\n");
return;
}
printf("Yes\n");
}
void Input()
{
ch = getchar();
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
a[ch-'0']++;
}
ch = getchar();
while(scanf("%c", &ch)==1)
{
if(ch<'0' || ch>'9') break;
b[ch-'0']++;
}
}
int main()
{
scanf("%d", &T);
ch = getchar();
while(T--)
{
memset(a, 0, sizeof(a)); //记得每次循环数组都要清零
memset(b, 0, sizeof(b));
Input(); //读入函数
check(); //判断并输出
}
return 0;
}
/*
输入样例:
4
12345678
85673421
12131416
63421111
46783
60708304
56793200
68579320
*/
然后更新一下字符串做法
#include<stdio.h>
#include<string.h>
#define maxn 105
#define FOR(a, b, c) for(int a=b; a<=c; a++)
int T, a[maxn], b[maxn];
char s1[maxn], s2[maxn];
void check()
{
//FOR(j, 1, 9) printf("%d ", a[j]); printf("\n");
//FOR(j, 1, 9) printf("%d ", b[j]); printf("\n");
FOR(i, 1, 9)
if(a[i]!=b[i])
{
printf("No\n");
return;
}
printf("Yes\n");
}
void Input()
{
scanf("%s", &s1);
scanf("%s", &s2);
int len1 = strlen(s1);
int len2 = strlen(s2);
FOR(i, 0, len1-1)
a[s1[i]-'0']++;
FOR(i, 0, len2-1)
b[s2[i]-'0']++;
}
int main()
{
scanf("%d", &T);
while(T--)
{
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(s1, 0, sizeof(s1));
memset(s2, 0, sizeof(s2));
Input();
check();
}
return 0;
}
/*
4
12345678
85673421
12131416
63421111
46783
60708304
56793200
68579320
*/
第一次写博客,markdown真好玩,估计之后会玩很久hhhh