第三章 数组和字符串(习题篇)--算法竞赛入门经典

第三章 数组和字符串(习题篇)

习题3-1:得分(Score,UVa1585)

给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现的O的个数,X的得分为0。例如,OOXXOXXOOO的得分1+2+0+0+1+0+0+1+2+3。
分析 :输入字符串后从字符串开头循环,是‘X’就把O的权值改为0;是‘O’就权值加1,答案加上权值。最后输出答案。

#include <stdio.h>
#include<string.h>
#define maxn 10000
int main()
{
	/*freopen("input.txt", "r", stdin);*/
	char s[maxn];
	scanf("%s", s);
	int kase = 0;
	int tran = 0;
	for (int i = 0;i < strlen(s);i++)
	{
		if (kase)
			printf("+");
		else
			kase = 1;
		if (s[i] == 'O')
		{
			tran++;
		}
		else
		{
			tran = 0;
		}
		printf("%d", tran);
	}
	printf("\n");
	return 0;
}
习题3-2:分子量(Molar Mass,UVa1586)

给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01(单位:g/mol),输入t个分子式,输出分子量,保留三位小数。
输入:
C
C6H5OH
NH2CH2COOH
C12H22O11
输出:
12.010
94.108
75.070
342.296

#include <stdio.h>
#include<ctype.h>
#define maxn 10000

int main()
{
  /*freopen("input.txt", "r", stdin);*/
  char s[maxn];
  scanf("%s", s);
  int i = 0;
  double sum = 0;
  double temp;
  while (s[i])
  {
  	switch (s[i])
  	{
  	case 'C':
  		temp = 12.01;
  		break;
  	case 'H':
  		temp = 1.008;
  		break;
  	case 'O':
  		temp = 16.00;
  		break;
  	case 'N':
  		temp = 14.01;
  		break;
  	}
  	int num = 0;
  	while (isdigit(s[i + 1]))
  	{
  		num = num * 10 + (s[++i] - '0');
  	}
  	if (!num)
  	{
  		num = 1;
  	}
  	temp = temp*num;
  	sum += temp;
  	i++;
  }
  printf("%.3lfg/mol\n", sum);
  return 0;
}
习题3-3:数数字(Digit Counting,UVa1225)

把前n(n<=100000)个整数顺序写在一起,123456789…数一数0-9各出现多少次。

#include <stdio.h>
#include<string.h>
#define maxn 100001

int main()
{
	/*freopen("input.txt", "r", stdin);*/
	char s[maxn];
	int count[10];
	memset(count, 0, sizeof(count));
	scanf("%s", s);
	for (int i = 0;i < strlen(s);i++)
	{
		count[s[i] - '0']++;
	}
	for (int i = 0;i < 10;i++)
	{
		printf("%d ", count[i]);
	}
	printf("\n");
	return 0;
}
习题3-4:周期串(Periodic Strings,UVa455)

如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为最小周期(注意,它也以6和12为周期)。输入一个长度不超过80的字符串,输出其最小周期。
答案一:

#include <stdio.h>
#include<string.h>
#define maxn 81

int main()
{
	/*freopen("input.txt", "r", stdin);*/
	char s[maxn];
	scanf("%s", s);
	int p = 0, q = 0, i = 0, j = 0;
	int n = strlen(s);
	for (i = 0;i < n;i++)
	{
		j = n % (i + 1);
		if (j != 0)
		{
			continue;
		}
		j = n / (i + 1);
		for (p = 0;p < j;p++)
		{
			for (q = 0;q <= i;q++)
			{
				if (s[q] != s[p*(i + 1) + q])
				{
					q = i + 1;//退出循环for (q = 0;q <= i;q++)
					p = j + 1;//退出循环for (p = 0;p < j;p++)
				}
			}
		}
		if (p == j)
		{
			break;
		}
	}
	i++;
	printf("最小周期是:%d\n", i);
	return 0;
}

答案二:

#include <stdio.h>
#include<string.h>
#define maxn 81

int main()
{
	/*freopen("input.txt", "r", stdin);*/
	char s[maxn];
	scanf("%s", s);
	int i = 0, j = 0;
	int n = strlen(s);
	for (i = 1;i <= n;i++)
	{
		j = n % i;
		if (j != 0)
		{
			continue;
		}
		bool isBreak = false;
		for (j = i;j < n;j++)
		{
			if (s[j] != s[j%i])
			{
				isBreak = true;
				break;
			}
		}
		if (!isBreak)
		{
			break;
		}
	}
	printf("最小周期是:%d\n", i);
	return 0;
}
习题3-5:谜题(Puzzle,UVa227)

有一个5*5的网格,其中恰好有一个格子是空的,其他格子各有一个字母,一共有四种指令:A,B,L,R,分别表示把空格上、下、左、右的相邻字母移到空格中。输入初始网格和指令序列(分别以数字0结束),输出指令执行完毕后的网格。如果有非法指令,应输出"This puzzle has no final configuration.",例如,图执行ARRBBL0。

题目分析:题目需要做的事情
①输入55的字符,每个格子有一个字母,有一个格子是空格
②找出输入字符空格的具体位置,保存下来
③之后分别检测指令A,B,L,R,这里还需要注意边界的地方,
④最后根据指令进行移动,最后从新输出每个格子的每个字符

#include <stdio.h>
#include <string.h>
#define maxn 100
int main()
{
	/*freopen("input.txt", "r", stdin);*/
	char cmd[maxn];
	char s[5][5] = { 'T','R','G','S','J',
		'X','D','O','K','I',
		'M',' ','V','L','N',
		'W','P','A','B','E',
		'U','Q','H','C','F' };
	int x, y;
	for (int i = 0;i < 5;i++)
	{
		for (int j = 0;j < 5;j++)
		{
			if (s[i][j] == ' ')
			{
				x = i;
				y = j;
			}
			printf("%2c", s[i][j]);
		}
		printf("\n");
	}
	while (scanf("%s", cmd) == 1)
	{
		int i = 0;
		while (cmd[i] != '0')
		{
			if (cmd[i] == 'A'&&x != 0)
			{
				s[x][y] = s[x - 1][y];
				s[--x][y] = ' ';
			}
			else if (cmd[i] == 'B'&&x != 4)
			{
				s[x][y] = s[x + 1][y];
				s[++x][y] = ' ';
			}
			else if (cmd[i] == 'L'&&y != 0)
			{
				s[x][y] = s[x][y - 1];
				s[x][--y] = ' ';
			}
			else if (cmd[i] == 'R'&&y != 4)
			{
				s[x][y] = s[x][y + 1];
				s[x][++y] = ' ';
			}
			else
			{
				printf("This puzzle has no final configuration.\n");
				cmd[i] = '0';
				break;
			}
			i++;
		}
		printf("After %s\n", cmd);
		for (int i = 0;i < 5;i++)
		{
			for (int j = 0;j < 5;j++)
			{
				printf("%2c", s[i][j]);
			}
			printf("\n");
		}
	}

	return 0;
}
习题3-6:纵横字谜的答案(Crossword Answers,UVa232)

输入一个r行c列(1<=r,c<=10)的网格,黑格用‘’表示,每个白格都填有一个字母。如果一个白格的左边相邻位置或者上边相邻位置没有白格(可能是黑格,也可能出了网格边界),则称这个白格是一个起始格。首先把所有的起始格从上到下,从左到右的顺序编号为1,2,3…。
接下来要输出所由横向单词,这些单词必须从一个起始格开始,向右延伸到一个黑格的左边,或者整个网格的最右列。最后找出所有竖向单词。这些单词必须从一个起始格开始,向下延伸到一个黑格的上边或者整个网格的最下行。
样例输入:
2 2
AT
O
6 7
AIM
DEN
MEONE
UPON
TO
SOERIN
SAOR

IES*DEA
0
样例输出:
puzzle #1:
Across
1.AT
3.O
Down
1.A
2.TO

puzzle #2:
Across
1.AIM
4.DEN
7.ME
8.ONE
9.UPON
11.TO
12.SO
13.ERIN
15.SA
17.OR
18.IES
19.DEA
Down
1.A
2.IMPOSE
3.MEO
4.DO
5.ENTIRE
6.NEON
9.US
10.NE
14.ROD
16.AS
18.I
20.A

思路一:
1.横向的时候每行扫描数组;
2.找到起始格并计数,输出每个字母,碰到和边界则输出换行;
3.纵向的时候也是每行扫描数组;
4.找到起始格并计数,向下输出单词并把输出过的字母赋值为0,直到碰到
和边界输出换行;
5.赋值为0的起始格不需要向下输出单词。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define maxn 1000
int main()
{
	freopen("input.txt", "r", stdin);
	int r, c;
	char line[maxn];
	int num = 0;
	while (scanf("%d", &r) == 1 && r >= 1 && scanf("%d", &c) == 1)
	{
		printf("\npuzzke #%d\n", ++num);
		char * ch1 = (char*)malloc(r*c*sizeof(char));
		for (int i = 0;i < r;i++)
		{
			scanf("%s", line);
			for (int j = 0;j < c;j++)
			{
				*(ch1 + i*c + j) = line[j];
			}
		}
		int count = 0;
		printf("Across:\n");
		for (int i = 0;i < r;i++)
		{
			for (int j = 0;j < c;j++)
			{
				if ((*(ch1 + i*c + j) != '*') && (j == 0 || *(ch1 + i*c + j - 1) == '*' || i == 0 || *(ch1 + (i - 1)*c + j) == '*'))
				{
					count++;
					printf("  %d.%c", count, *(ch1 + i*c + j));
					j++;
					while (j < c&&*(ch1 + i*c + j) != '*')
					{
						if (i == 0 || *(ch1 + (i - 1)*c + j) == '*')
						{
							count++;
						}
						printf("%c", *(ch1 + i*c + j));
						j++;
					}
					printf("\n");
				}
			}
		}
		count = 0;
		printf("Down:\n");
		for (int i = 0;i < r;i++)
		{
			for (int j = 0;j < c;j++)
			{
				if ((*(ch1 + i*c + j) != '*') && (j == 0 || *(ch1 + i*c + j - 1) == '*' || i == 0 || *(ch1 + (i - 1)*c + j) == '*'))
				{
					count++;
					if (*(ch1 + i*c + j) == '0')
					{
						continue;
					}
					printf("  %d.%c", count, *(ch1 + i*c + j));
					int y = i + 1;
					while (y < r && (*(ch1 + y*c + j) != '*'))
					{
						printf("%c", *(ch1 + y*c + j));
						*(ch1 + y*c + j) = '0';
						y++;
					}
					printf("\n");
				}
			}
		}
		free(ch1);
	}
	return 0;
}

思路二:
1.横向的时候每行扫描数组;
2.分辨出起始格、号格和不是起始格的白格;
3.是
号格不做处理继续循环查找;
4.是起始格的话就计数,进一步判断是否是一个单词的开头还是单词开头字母后的字母;
5.是不是起始格的白格直接输出字母;
6.纵向的时候也是每行扫描数组;
7.分辨出号格和起始格;
8.是
号格不做处理继续循环查找;
9.是起始格计数并判断是否是一个单词的开头,是开头字母则往后输出单词。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define maxn 1000
int main()
{
	freopen("input.txt", "r", stdin);
	int r, c;
	char line[maxn];
	int num = 0;
	while (scanf("%d", &r) == 1 && r >= 1 && scanf("%d", &c) == 1)
	{
		printf("\npuzzke #%d\n", ++num);
		char * ch1 = (char*)malloc(r*c*sizeof(char));
		for (int i = 0;i < r;i++)
		{
			scanf("%s", line);
			for (int j = 0;j < c;j++)
			{
				*(ch1 + i*c + j) = line[j];
			}
		}
		int count = 0;
		printf("Across:");
		for (int i = 0;i < r;i++)
		{
			for (int j = 0;j < c;j++)
			{
				if (*(ch1 + i*c + j) == '*')
					continue;
				if (j == 0 || *(ch1 + i*c + j - 1) == '*' || i == 0 || *(ch1 + (i - 1)*c + j) == '*')
				{
					count++;
					if (j == 0 || *(ch1 + i*c + j - 1) == '*')
					{
						printf("\n  %d.%c", count, *(ch1 + i*c + j));
					}
					else
					{
						printf("%c", *(ch1 + i*c + j));
					}
				}
				else
				{
					printf("%c", *(ch1 + i*c + j));
				}
			}
		}
		count = 0;
		printf("\nDown:");
		for (int i = 0;i < r;i++)
		{
			for (int j = 0;j < c;j++)
			{
				if (*(ch1 + i*c + j) == '*')
					continue;
				if (j == 0 || *(ch1 + i*c + j - 1) == '*' || i == 0 || *(ch1 + (i - 1)*c + j) == '*')
				{
					count++;
					if (i == 0 || *(ch1 + (i - 1)*c + j) == '*')
					{
						printf("\n  %d.%c", count, *(ch1 + i*c + j));
						int y = i + 1;
						while (y < r && (*(ch1 + y*c + j) != '*'))
						{
							printf("%c", *(ch1 + y*c + j));
							y++;
						}
					}
				}
			}
		}
		printf("\n");
		free(ch1);
	}
	return 0;
}
习题3-7:DNA序列(DNA Consensus String,UVa1368)

给定m个长度为n的DNA序列,求一个DNA序列,使其到所有这些序列的总hamming距离尽量小,如果有多个解,输出字典顺序的最小解。(hamming距离是两个序列不同字符的个数)。
输入:
5 8
TATGATAC
TAAGCTAC
AAAGATCC
TGAGATAC
TAAGATGT
输出:
TAAGATAC
思路:用数组统计各列各类字符的个数,然后取每一列出现次数最多的字符,拼接成即可,如果次数相同则ASCI码小的优先。

#include <stdio.h>
#include<string.h>
#include<stdlib.h>
#define maxn 1000
char line[maxn];
int main()
{
	freopen("input.txt", "r", stdin);
	int m, n;
	scanf("%d%d", &m, &n);
	char *s = (char *)malloc(m*n*sizeof(char));
	char *mark = "ACGT";
	int ans[4];
	int max, index;
	memset(ans, 0, sizeof(ans));
	for (int i = 0;i < m;i++)
	{
		scanf("%s", line);
		for (int j = 0;j < n;j++)
		{
			*(s + i*n + j) = line[j];
		}
	}
	for (int i = 0;i < n;i++)
	{
		memset(ans, 0, sizeof(ans));
		for (int j = 0;j < m;j++)
		{
			if (*(s + j*n + i) == 'A')
			{
				ans[0]++;
			}
			if (*(s + j*n + i) == 'C')
			{
				ans[1]++;
			}
			if (*(s + j*n + i) == 'G')
			{
				ans[2]++;
			}
			if (*(s + j*n + i) == 'T')
			{
				ans[3]++;
			}
		}
		max = ans[0];
		index = 0;
		for (int z = 1;z < 4;z++)
		{
			if (max <= ans[z])
			{
				if (max == ans[z])
				{
					if (mark[z] - mark[max] > 0)
						continue;
				}
				max = ans[z];
				index = z;
			}
		}
		printf("%c", mark[index]);
	}
	printf("\n");
	free(s);
	return 0;
}
习题3-8:循环小数(Repeating Decimals,UVa202)

输入整数a和b(0<=a<=3000,1<=b<=3000),输出a/b的循环小数表示以及其循环节长度。例如 a=5 b=43 小数表示为0.(116279069767441860465),循环节长度为21。
思路:模拟长除法的计算过程。
①mod = a%b;
②小数 = (mod10) / b;
③mod = (mod
10)%b;
循环②③步,当出现重复的余数的时候,也就是循环节出现了。如果是无理数的时候在0~b-1个小数后必会出现循环(出现相同的余数)。

#include <stdio.h>
#include<string.h>
#define maxn 3000
bool line[maxn];
int main()
{
	/*freopen("input.txt", "r", stdin);*/
	int a, b;
	scanf("%d%d", &a, &b);
	memset(line, 0,sizeof(line));
	int count = 0;
	int mod;
	mod = a%b;
	printf("%d", a / b);
	if (mod == 0)
	{
		printf("\n");
	}
	else
	{
		printf(".(");
		while (mod && !line[mod])
		{
			count++;
			line[mod] = true;
			a = (mod * 10) / b;
			mod = (mod * 10) % b;
			printf("%d", a);
		}
		printf(") %d\n", count);
	}
	return 0;
}

输入:
5 43
输出:
0.(116279069767441860465) 21

习题3-9:子序列(All in All,UVa10340)

输入s和t两个字符串,t通过删除0个或一些字符能够得到s。
思路:从t的左边遍历到右边,找到s的第一个字母后,继续往后找到s的第二个字母,如果能找到s中的全部字母,则t通过删除0个或一些字符能够得到s。

#include <stdio.h>
#include<string.h>
#define maxn 1000
int main()
{
	/*freopen("input.txt", "r", stdin);*/
	char s[maxn];
	char t[maxn];//从t中剔除
	printf("Put in a string:\n");
	scanf("%s", t);
	printf("Put in a substring:\n");
	scanf("%s", s);
	int len_t, len_s;
	int i, j;
	len_t = strlen(t);
	len_s = strlen(s);
	for (i = 0, j = 0;(j < len_s) /*&& (i < len_t)*/ && (len_s - j) <= (len_t - i);i++)
	{
		if (t[i] == s[j])
			j++;
	}
	if (j == len_s)
	{
		printf("Find!\n");
	}
	else
	{
		printf("No find!\n");
	}
	return 0;
}
习题3-10:盒子(Box,UVa1587)

给定6个矩形的长和宽wi和hi(1≤wi,hi≤1000),判断它们能否构成长方体的6个面。
思路一:
1.使用一个2维数组,找到两个相同的面并将其排在一起,找不到两个相同的面就不能构成长方形;
2.第一个面的一条边在第二个面有一条相同的边,第一个面的另一个边在三个边有条相同的边,第二个面和第三个面剩下的边也相同。

#include <stdio.h>
int main()
{
	freopen("input.txt", "r", stdin);
	int a[6][2];
	int temp;
	bool flag;
	for (int i = 0;i < 6;i++)
	{
		scanf("%d", &a[i][0]);
		scanf("%d", &a[i][1]);
	}
	for (int i = 0;i < 6;i+=2)
	{
		flag = false;
		for (int j = i + 1;j < 6;j++)
		{
			if (a[i][0] == a[j][0] && a[i][1] == a[j][1])
			{
				temp = a[i + 1][0];
				a[i + 1][0] = a[j][0];
				a[j][0] = temp;
				temp = a[i + 1][1];
				a[i + 1][1] = a[j][1];
				a[j][1] = temp;
				flag = true;
				break;
			}
			else if (a[i][0] == a[j][1] && a[i][1] == a[j][0])
			{
				temp = a[i + 1][0];
				a[i + 1][0] = a[j][1];
				a[j][1] = temp;
				temp = a[i + 1][1];
				a[i + 1][1] = a[j][0];
				a[j][0] = temp;
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			printf("Impossible!\n");
			return 0;
		}
	}
	if (a[0][0] == a[2][0])
	{
		if (a[0][1] == a[4][0] && a[2][1] == a[4][1])
		{
			printf("Possible!\n");
		}
		else if (a[0][1] == a[4][1] && a[2][1] == a[4][0])
		{
			printf("Possible!\n");
		}
		else
		{
			printf("Impossible!\n");
		}
	}
	else if (a[0][0] == a[2][1])
	{
		if (a[0][1] == a[4][0] && a[2][0] == a[4][1])
		{
			printf("Possible!\n");
		}
		else if (a[0][1] == a[4][1] && a[2][0] == a[4][0])
		{
			printf("Possible!\n");
		}
		else
		{
			printf("Impossible!\n");
		}
	}
	else if (a[0][0] == a[4][0])
	{
		if (a[0][1] == a[2][0] && a[2][1] == a[4][1])
		{
			printf("Possible!\n");
		}
		else if (a[0][1] == a[2][1] && a[2][0] == a[4][1])
		{
			printf("Possible!\n");
		}
		else
		{
			printf("Impossible!\n");
		}
	}
	else if (a[0][0] == a[4][1])
	{
		if (a[0][1] == a[2][0] && a[2][1] == a[4][0])
		{
			printf("Possible!\n");
		}
		else if (a[0][1] == a[2][1] && a[2][0] == a[4][0])
		{
			printf("Possible!\n");
		}
		else
		{
			printf("Impossible!\n");
		}
	}
	else
	{
		printf("Impossible!\n");
	}

	return 0;
}

思路二:先是在输入数据的时候就先对其作出处理,保证x<y; 再是对存储在结构体中的数据排序。 如果x相同,则按照y的值从小到大排序;否则按照x的值从小到大排序。

?????
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值