这次开始对字符串的探究及学会应用
这次开始对字符串的探究及学会应用
目录
做几道题
N-31. 字符串比较
#include<stdio.h>
#include<string.h>
int main() {
char s1[50];
char s2[50];
char s3[50];
gets(s1);
gets(s2);
gets(s3);
int ret = 0;
if (strlen(s1) > strlen(s2)) {
if (strlen(s2) > strlen(s3)) {
ret = 3;
} else if (strlen(s2) < strlen(s3)) {
ret = 2;
} else {
if (strcmp(s2, s3) < 0) {
ret = 3;
} else if (strcmp(s2, s3) > 0) {
ret = 2;
} else {
ret = 2;
}
}
} else if (strlen(s1) < strlen(s2)) {
if (strlen(s1) > strlen(s3)) {
ret = 3;
} else if (strlen(s1) < strlen(s3)) {
ret = 1;
} else {
if (strcmp(s1, s3) < 0) {
ret = 3;
} else if (strcmp(s1, s3) > 0) {
ret = 1;
} else {
ret = 1;
}
}
} else {
if (strcmp(s1, s2) < 0) {
ret = 2;
} else if (strcmp(s1, s2) > 0) {
ret = 1;
} else {
ret = 1;
}
}
switch (ret) {
case 1:
puts(s1);
break;
case 2:
puts(s2);
break;
case 3:
puts(s3);
break;
default:
break;
}
}
N-32.爱丽丝与电脑病毒
电脑病毒是编制者在计算机程序中插入的破坏计算机功能或者数据的代码。最近,爱丽丝对这个概念产生了浓厚的兴趣。
一般来说,电脑病毒可以实现代码的生成和复制。要实现这个功能,需要电脑病毒程序本身会自动生成一段代码源文件,然后交给编译器编译运行。有了这个功能,电脑病毒可以将相似的代码执行任意次,因此拥有非常强的破坏力。
现在爱丽丝想制作一个电脑病毒,这个电脑病毒可以输出如下的一段代码源文件:
\#include<stdio.h>
int main()
{
int n,i;
scanf("%d",&n);
for(i=0;i<n;i++){
printf("xxx\n");
}
}
输出的代码源文件的意思是,输入一个整数n,然后将字符串"xxx"打印n行。而里面的"xxx"可以替换成其他的字符串。
现在你要帮助爱丽丝实现这个功能:给定一行字符串s,按照上述格式输出相应的代码源文件,而代码源文件实现的是将字符串s打印n行。
输入格式:
一行字符串s,代表输出的源代码文件要打印的字符串(字符串中间可能有空格),字符串以换行符结尾。
输出格式:
一个代码源文件,输出格式见题目背景和样例输出。生成的代码源文件的最后一行以换行符结尾。
样例输入:
hello world!
样例输出:
\#include<stdio.h>
int main()
{
int n,i;
scanf("%d",&n);
for(i=0;i<n;i++){
printf("hello world!\n");
}
}
数据范围:
字符串s的长度<=10000
注意事项:
为了比对文件方便,输出程序的tab用四个空格来代替,输出代码源文件的最后一行以换行符结尾
#include<stdio.h>
#include<string.h>
/*input
hello world!
*/
/*output
#include<stdio.h>
int main()
{
int n,i;
scanf("%d",&n);
for(i=0;i<n;i++){
printf("hello world!\n");
}
}
*/
int main(){
char s[20000];
gets(s);
printf("#include<stdio.h>\nint main()\n{\n int n,i;\n scanf(\"%%d\",&n);\n for(i=0;i<n;i++){\n printf(\"");
printf("%s",s);
printf("\\n\");\n }\n}\n");
}
真正的注意事项:
-
输出英文双引号时记得之前加上反斜杠即\"
-
输出反斜杠也要记得加多一次反斜杠如\\
-
输出%记得加上%,如%%
-
另外记得把最初的字符数组比给出的要求范围定义大一点
-
另外如果一筹莫展时建议用八进制搞
-
再者输出%d这种需要修饰符时可以用*,有意思(可以试试下面代码)
#include<stdio.h> #include<string.h> int main(){ for(int i=0;i<300;i++){ printf("%*c`-(^.^)-、",i,' '); } }
N-33. 密码评估
题目描述
很多时候我们都需要密码来保障自己的财产安全,而不同密码由于复杂程度不同,它被破译的可能性也不同,其密码强度也不一样。对于一串密码,其密码强度的规则如下:密码至少要有6位,且包含数字和字母,否则该密码无效;在有效密码中,若密码位数大于10位、大小写字母均有、含有特殊符号这三种条件均不满足,该密码强度为弱;满足这三种条件任意一个或者两个,则该密码强度为中;三种条件均满足,该密码强度为强。现给你一系列用户所设置的密码,要求你按规定评估出其密码强度。
输入格式
输入第一行为一个正整数T,表示接下来将有T个密码。
接下来T行,第i行首先是一个整数,表示第i个密码的长度,接下来是一个长度为n的字符串,表示要评估的密码。保证密码为键盘上可以打出的符号且非空格。
输出格式
输出应该有T行。
第i行表示所评估的第i个密码的强度。若为无效密码,则输出”INVALID”;若强度为弱,输出”RUO”;强度为中,输出”ZHONG ”;强度为强,输出”QIANG”.
样例输入
4
6 123456
9 bityes123
9 BITyes123
11 BITyes123##
样例输出
INVALID
RUO
ZHONG
QIANG
数据范围
0<T≤1000,0<n≤20
——by GodWrath
#include<stdio.h>
#include<string.h>
int judgetype(char a[], int n) {
int ret0 = 0, ret1 = 0, ret2 = 0, ret3 = 0;
if (n >= 6) {
ret0 = 1;
}
for (int i = 0; i < n; i++) { //判断格式正确与否
if (a[i] >= '0' && a[i] <= '9') {
ret1 = 1;
}
if (a[i] >= 'A' && a[i] <= 'Z') {
ret2 = 1;
}
if (a[i] >= 'a' && a[i] <= 'z') {
ret3 = 1;
}
}
return ret0 && ret1 && (ret2 || ret3);
}
int judgelevel(char a[], int n) {
int ret0 = 0, ret1 = 0, ret2 = 0, ret3 = 0, ret4 = 0;
int level = 0;
if (n > 10) {
ret0 = 1;
}
for (int i = 0; i < n; i++) { //级别
if (a[i] >= '0' && a[i] <= '9') {
ret1 = 1;
}
if (a[i] >= 'A' && a[i] <= 'Z') {
ret2 = 1;
}
if (a[i] >= 'a' && a[i] <= 'z') {
ret3 = 1;
}
if (!(a[i] >= '0' && a[i] <= '9') && !(a[i] >= 'A' && a[i] <= 'Z') && !(a[i] >= 'a' && a[i] <= 'z')) {
ret4 = 1;
}
}
level = ret0 + (ret2 && ret3) + ret4;
return level;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d ", &n);
char a[n];
gets(a);
if (judgetype(a, n)) {
switch (judgelevel(a, n)) {
case 0:
printf("RUO");
break;
case 1:
printf("ZHONG");
break;
case 2:
printf("ZHONG");
break;
case 3:
printf("QIANG");
break;
default:
break;
}
printf("\n");
} else {
printf("INVALID\n");
}
}
}
这道题为啥有种数字信号处理的感觉,感觉自己写出这种想法有点奇妙其实;
N-34. Deoxynucleotide sequence
Description
限制酶SmaⅠ可识别的脱氧核苷酸序列为CCCGGG,并切割C-G处磷酸二酯键。现实验室拟使用人工法逆序连接充分切割后的DNA片段。请设计程序,给出重组后的DNA序列。
Input
共两行,两个字符串,即切割前DNA双链的脱氧核苷酸序列.
Output
共两行,输出重组后的DNA双链的脱氧核苷酸序列.
Sample Input
ACCCGGGTCCCGGGTT
TGGGCCCAGGGCCCAA
Sample Output
GGGTTGGGTCCCACCC
CCCAACCCAGGGTGGG
Hint
样例中切割片段依次为:
ACCC GGGTCCC GGGTT
TGGG CCCAGGG CCCAA
逆序连接后的DNA序列为:
GGGTT|GGGTCCC|ACCC
CCCAA|CCCAGGG|TGGG
保证输入DNA序列长度不超过100,且字符串仅含A、T、C、G四个字符。
切割和重组操作是独立的,即重组DNA允许含有CCCGGG序列
#include<stdio.h>
#include<string.h>
/*
ACCCGGGCCCGGGGGGCCCGGGACCC
TGGGCCCGGGCCCCCCGGGCCCTGGG
*/
/*
ACCC GGG CCC GGGGGG CCC GGGACCC
TGGG CCC GGG CCCCCC GGG CCCTGGG
*/
/*
GGGACCC CCC GGGGGG CCC GGG ACCC
CCCTGGG GGG CCCCCC GGG CCC TGGG
*/
int* findindex(char target[], char a1[], int location[], int *num_target) { //从左至右单向搜索字符串,返回断开DNA处索引
//现在我们需要双向索引,因为发现反向的案例也符合,就是以后审题时要看多个样例,避免自己理解有误白敲代码
int len = strlen(a1);
int targetlen = strlen(target);
int cnt = 0;
for (int i = 0; i <= len - targetlen; i++) {
int ret1 = 0, ret2 = 0;
for (int j = 0, temp = i; j < targetlen; j++, temp++) {
if (target[j] == a1[temp]) {
ret1++;
}
if (target[targetlen - 1 - j] == a1[temp]) {
ret2++;
}
}
if (ret1 == 6 || ret2 == 6) { //记录索引方便操作
location[++cnt] = i + 3;
}
}
location[++cnt] = len ; //希望得到一个索引数组
*num_target = cnt + 1; //同时传回索引数目
return location;
}
int main() {
char a1[200], a2[200];
gets(a1);
gets(a2);
char target[7] = "CCCGGG";
int location[50] = {0};
int num_target = 0;
findindex(target, a1, location, &num_target);
int len = strlen(a1);
for (int i = 0; i < num_target ; i++) {
printf("|%-*d", location[i + 1] - location[i], location[i]);
}
printf("\n");
for (int i = 0; i < num_target ; i++) {
printf("|");
for (int j = location[i]; j < location[i + 1]; j++) {
printf("%c", a1[j]);
}
}
printf("\n");
for (int i = 0; i < num_target ; i++) {
printf("|");
for (int j = location[i]; j < location[i + 1]; j++) {
printf("%c", a2[j]);
}
}
printf("\n");
for (int i = num_target-1; i >=0 ; i--) {
for (int j = location[i]; j < location[i + 1]; j++) {
printf("%c", a1[j]);
}
}
printf("\n");
for (int i = num_target-1; i >=0 ; i--) {
for (int j = location[i]; j < location[i + 1]; j++) {
printf("%c", a2[j]);
}
}
printf("\n");
}
运行结果:
ACCCGGGCCCGGGGGGCCCGGGACCC
TGGGCCCGGGCCCCCCGGGCCCTGGG
|0 |4 |7 |10 |16 |19 |26
|ACCC|GGG|CCC|GGGGGG|CCC|GGGACCC|
|TGGG|CCC|GGG|CCCCCC|GGG|CCCTGGG|
GGGACCCCCCGGGGGGCCCGGGACCC
CCCTGGGGGGCCCCCCGGGCCCTGGG
CCGG
GGCC
|0 |4
|CCGG|
|GGCC|
CCGG
GGCC
ACCCGGGCCCGGG
TGGGCCCGGGCCC
|0 |4 |7 |10 |13
|ACCC|GGG|CCC|GGG|
|TGGG|CCC|GGG|CCC|
GGGCCCGGGACCC
CCCGGGCCCTGGG
总结及注意事项:
- 真的得看清楚案例的真正意思再来实现功能,不要想当然地先写出代码再改;
- 一点一点写,反复输出确认;
- 这个锻炼了我们很多字符串的知识:在字符串搜索子字符串,并返回索引;(额外的反向搜索就很简单了)
- 本来有strstr这种搜索的函数,但别人当时没想着用;
- 另外在创建字符串数组时同样把它创建比给出的范围大个一两倍,通常onlinejudge返回内存错误都是指溢出;(数组太小)
额外知识
strcpy机制探索:覆盖或重写?
#include<stdio.h>
#include<string.h>
int main() {
char s0[10]="123456789";
char s1[10]="ABC";
strcpy(s0,s1);
}
执行次数 | s0,s1内部 |
---|---|
开始第4行之前 | s0 = "\000\000D\000\000\000,/\260" s1 = "\240\026@\000\060\026@\000\000" |
执行第四行 | s0 = "123456789" s1 = "\240\026@\000\060\026@\000\000" |
执行第五行 | s0 = "123456789" s1 = "ABC\000\000\000\000\000\000" |
执行第六行 | s0 = "ABC\000\065\066\067\070\071" s1 = "ABC\000\000\000\000\000\000" |
结束 | No symbol table info available. No symbol table info available. |
通过调试和对照ascii码表,我们知道这就是覆盖的意思,“123”的位置被“ABC”替代了,“\0”代替了“4”,之后的诸如\065是八进制的意思,转换成十进制53,对应的字符就是5,后面以此类推;
换种角度,如果我是C语言发明者,我肯定也会这样设置,因为这样明显节约操作内存,而且毫无危险,重写相当于对\000后的内存做无用功;