第3章 数组和字符串

第3章 数组和字符串

学习要求和笔记:

√  掌握一维数组的声明和使用方法

√  掌握二维数组的声明和使用方法

√  掌握字符串的声明、赋值、比较和连接方法

√  熟悉字符的ASCII码和ctype.h中的字符函数

√  正确认识“++”、“+=”等能修改变量的运算符

√  掌握fgetc 和 getchar的使用方法

√ 了解不同操作系统中换行符的表示方法

√ 掌握fgets的使用方法并了解gets的“缓冲去溢出”漏洞

√ 学会用常量表来简化代码

例题学习记录和源代码:

√  Example3-1 逆序输出

√  Example3-2 开灯问题

√  Example3-3 蛇形填数

√  Example3-4 竖式问题

√  Example3-5 Tex中的引号(UVa 272)

√  Example3-6 WERTYU (UVa10082)

√  Example3-7 回文词 Palindromes(UVa401)

√  Example3-8 猜数字游戏的提示 (UVa 340)

√  Example3-9 生成元Digit Generator (UVa1583)

√  Example3-10 环形序列Circular Sequence (UVa1584)


// Example_0301_逆序输出_数组.cpp  
  
/** 
 * 题目名称:逆序输出 
 * 题目描述:将数组内的数逆序输出。 
 * 学习笔记:
 * ①把int a[maxn]放在外面是有原因的,而且最好把数组定义的稍微大些,全局变量在静态存储区分配内存;局部变量在栈上分配内存空间
 * ②把数据x存储在向数组中a[n++] = x
 * ③这里需要注意一个细节,就是输出格式行首和行尾不需要加空格,所以想到最后一个a[0]单独输出
 * ④
 **/  
/*
//.c
#include <stdio.h>
#define maxn 100 + 5
int a[maxn];

int main()
{
	int i,x,n = 0;
	while(scanf("%d", &x) == 1)
		a[n ++] = x;
	for(i = n - 1; i >= 1; i--)
		printf("%d ", a[i]);
	printf("%d\n", a[0]);
	return 0;
}
*/

//.cpp
#include <iostream>
using namespace std;
#define maxn 100 + 5
int a[maxn];

int main()
{
	int i,x,n = 0;
	while(cin >> x)
		a[n ++] = x;
	for(i = n - 1; i >= 1; i--)
		cout << a[i] << " ";
	cout << a[0] << endl;
	return 0;

}</span>

// Example_0302_开灯问题.cpp  
  
/** 
 * 题目名称:开灯问题 
 * 题目描述: 
 *      有n盏灯,编号为1~n。第1个人把所有灯打开,第2个人按下所有编号为2的总数的开关(这些灯将补关掉), 
 * 第3个人按下所有编号为3的倍数的开关(其中关掉的灯将被打开,开着的灯将被关掉),依此类推。 
 * 一共有k个人,问最后有哪此灯开着?输入n和k,输出开着的灯的编号。 k <= n <= 1000. 
 * 样例输入: 7 3 
 * 样例输出: 1 5 6 7 
 **/  
  
/** 
 * 题目分析: 
 *     这题并不难,只需要使用两个循环即可实现。 
 * 首先,我们可以先去理解一下开关灯实际上是什么,可以知道,开关灯,实际上是一种状态。 
 * 那么,我们可以使用一个数值来代表这种状态,下面的程序,用数值1表示开灯,数值0表示关灯。 
 * 由于这里有1 ~ n 盏灯,我们可以用一个数组来存放这n盏灯的信息。 
 * 解决开关灯状态问题后,可以开始算法思路: 
 * 题目中,要求第n个人就会将编号为n的倍数的灯的状态值改变,那么,我们可以在一个循环中负责实现“人”的改变。 
 * 然后,再在这个“人”的改变的循环中再嵌入一个处理”灯“的状态改变的循环函数。 
 * 学习笔记:
 * ①告诉我们一个思想,A随B去变化,我们就必须先循环负责B的改变,之后再循环中再去找A变化的函数。
 * ②这里教我们如何处理输出格式空格的问题,倒序那道题考虑的是,尾数据没有空格,所以把最后一个数据分开输出;
 *   这里的方法是假设一个first,这样可以判定如果是第一个数据,则不需要空格,其他都需要空格。
 * ③memset函数用来对一段内存空间全部设置为某个字符,一般用在定义的字符串进行初始化化'/0'
 *   原型:char a[100]; memset(a, 0, sizeof(a));
 **/ 
//.c
#include <stdio.h>
#include <string.h>
#define maxn 1010
int a[maxn];
int main()
{
	int i,j,n,k,first = 1;
	memset(a, 0, sizeof(a));
	scanf("%d%d",&n, &k);
	for(i = 1; i <= k; i++)
	{
		for(j = 1; j <= n; j++)
		{
			if(j % i == 0)
				a[j] = !a[j];
		}
	}

	for(i = 1; i <= n; i++)
	{
		if(a[i])
		{
			if(first)  //first作用设置当前输入变量为第一个,很巧妙
				first = 0;
			else 
				printf(" ");
			printf("%d", i);

		}
	}
	printf("\n");
	return 0;
}


//.cpp
#include <iostream>
#include <string.h>
using namespace std;
#define maxn 1010
int a[maxn];
int main()
{
	int i,j,n,k,first = 1;
	memset(a, 0, sizeof(a));
	cin >> n >> k;
	for(i = 1; i <= k; i++)
	{
		for(j = 1; j <= n; j++)
		{
			if(j % i == 0)
				a[j] = !a[j];
		}
	}

	for(i = 1; i <= n; i++)
	{
		if(a[i])
		{
			if(first)  //first作用设置当前输入变量为第一个,很巧妙
				first = 0;
			else 
				cout << " ";
			cout << i;

		}
	}
	cout << endl;
	return 0;
}

// Example3-3 蛇形填数.cpp  
  
/** 
 * 题目名称:蛇形填数 
 * 题目描述:在n*n方阵里填入1, 2, ..., n*n, (n <=8)要求填成蛇形(多余的空格只是为了便于观察规律,不必严格输出)。 
 * 样例输入: 4 
 * 样例输出: 
 *             10 11 12 1 
 *              9 16 13 2 
 *              8 15 14 3 
 *              7  6  5 4 
 **/  
  
/** 
 * 题目分析: 
 *     这里主要用到的是二维数组的知识,还有有点类似“迷宫”的一个判断方法。 
 *     首先原题说需要n*n (n <= 8)的方阵,那么,我们只需要定义一个a[n][n]的数组即可。 
 *     接下来,观察输出矩阵的蛇形规律,将数组中的每一元素初始化为0,将数字“1“从右上角开始填写, 
 * 然后,一直沿着 下 -> 左 -> 上 -> 右 的方向填充数字,直到全部0都填完为止。 
 * 那么,我们需要解决的问题,就只有如何能让它按这个方向走呢?我们可以将本来的0作为是否需要填充标记, 
 * 再将数组的边界作为另一个是否越界的标记。总之,就是根据这些标记,沿着指定方向,填充到不能填充为止。 
 * 最后,要解决的问题就是,如何知道它已经全部填充完了,然后跳出这个循环呢?我们可以用它本来需要填充的 
 * 数字个数作为标记,当它的数值小于方阵的总格子个数时,即符合要求。当然,需要注意的一个问题就是,当进行 
 * 确定它移动方向的判断时,需要先从数组边界作为越界条件去判断,然后再从数值上判断,这是为了防止内存溢出 
 * 的情况出现。 
 * 学习笔记:
 * ①填数问题,学会用数组来做,而且数组的值可以作为标记,还有数组的边界也可以作为标记,先把需要的值填入数组中
 *   之后再将这些数值按照一些输出格式进行输出。
 * ②填数问题,我们要注意先判断再填数,否则悔棋会很麻烦
 * ③关于输出格式问题,按一定长度输出,对于C而言 printf("%3d"....) 这里就是右对齐长度为3  printf("%-3d" 为左对齐
 *   c++而言,用setw(n)来控制,默认为右对齐,如果需要左对齐,加一个left,如cout << left << setw(n)
 **/  
  
/*
//.c
#include<stdio.h>
#include<string.h>

#define maxn 20
int a[maxn][maxn];

int main()
{
	int x,y,n,step;
	scanf("%d", &n);
	
	//对字符串初始化
	memset(a, 0, sizeof(a));
	step = a[x = 0][y = n-1] = 1;

	while(step < n*n)
	{
		//先向下
		while(x + 1 < n && !a[x + 1][y])
			a[++x][y] = ++step;

		//再向左
		while(y - 1 >= 0 && !a[x][y - 1])
			a[x][--y] = ++step;

		//之后向上
		while(x - 1 >= 0 && !a[x - 1][y])
			a[--x][y] = ++step;

		//之后向右
		while(y + 1 < n && !a[x][y + 1])
			a[x][++y] = ++step;
	}

	//输出
	for(x = 0; x < n; x++)
	{
		for(y = 0; y < n; y++)
			printf("%3d", a[x][y]);

		printf("\n");
	}
return 0;
}
*/
//.cpp
#include<iostream>
#include<string.h>
#include<iomanip>

using namespace std;
#define maxn 20
int a[maxn][maxn];

int main()
{
	int x,y,n,step;
	cin >> n;
	
	//对字符串初始化
	memset(a, 0, sizeof(a));
	step = a[x = 0][y = n-1] = 1;

	while(step < n*n)
	{
		//先向下
		while(x + 1 < n && !a[x + 1][y])
			a[++x][y] = ++step;

		//再向左
		while(y - 1 >= 0 && !a[x][y - 1])
			a[x][--y] = ++step;

		//之后向上
		while(x - 1 >= 0 && !a[x - 1][y])
			a[--x][y] = ++step;

		//之后向右
		while(y + 1 < n && !a[x][y + 1])
			a[x][++y] = ++step;
	}

	//输出
	for(x = 0; x < n; x++)
	{
		for(y = 0; y < n; y++)
			cout << left << setw(3) << a[x][y];

		cout << endl;
	}
return 0;
}

// Example_0304_竖式问题.cpp  
  
/** 
 * 题目名称:竖式问题 
 * 题目描述: 
 *      找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合。 
 * 输入数字集合(相邻数字之间没有空格),输出所有竖式。每个竖式前应有编号,之后应有一行空行。最后输出解的总数。 
 * (为了便于观察,竖式中的空格改用小数点显示,但你的程序应该输出空格,而非小数点)。 
 * 样例输入: 2357 
 * 样例输出: 
 * <1> 
 * ..775 
 * X..33 
 * ----- 
 * .2325 
 * 2325. 
 * ----- 
 * 25575 
 * 
 * The number of solutions = 1 
 **/  
  
 /** 
  * 题目分析: 
  * 尝试所有的abc和de,判断是否满足条件 if (" abc *de")是个合法的竖式)打印abc*de的竖式和其后的空行。 
  * 其实主要还是要找出主要需要解决的条件,在这题里面,需要解决的问题主要有两个。 
  * 第一个:输出格式控制,这个主要对cout或者prinf的使用方法熟悉即可, 
  * 第二个:“所有数字都属于一个特定的数字集合”,这里需要掌握筛选数字的技巧,下例中使用了“缓冲区”的方法。 
  *          首先,申请两个字符数组,第一个字符数组用于存放用户输入的字符,第二个字符数组用于存放进行乘法时出现过的数字的保存。 
  *          开始执行的过程, 
  *          第一步,将用户输入数字集合放到s字符组中。 
  *          第二步,计算,然后将计算时所有出现过的数字存放到另一个字符数组buf中。 
  *          第三步,开始逐个比较,判断是否buf中每个字符都能从s字符组中找到,起到了最终的筛选作用。 
  * 学习笔记:
  * ①#include <stdio.h>  int sprintf( char *buffer, const char *format, ... );  
  * sprintf()函数和printf()类似, 只是把输出发送到buffer(缓冲区)中.返回值是写入的字符数量.  
  * ②#include <string.h>  char *strchr( const char *str, int ch );  
  * 功能:函数返回一个指向str 中ch 首次出现的位置,当没有在str 中找ch到返回NULL。  
  * 在C++里面可以用#include <cstring>  size_type find( char ch, size_type index );  
  **/  

//.c
#include <stdio.h>
#include <string.h>
int main()
{
	int cnt = 0;
	char s[20], buf[99];

	scanf("%s", s);
	for(int abc = 111; abc <= 999; abc ++)
	{
		for(int de = 11; de <= 99; de ++)
		{
			//首先计算第一行乘积abc*e,之后第二行abc*d,最后是总乘积abc*de
			int x = abc * (de % 10), y = abc * (de / 10), z = abc * de;
			
			//把abc,de,x,y,z全部保存在buf字符串中
			sprintf(buf, "%d%d%d%d%d", abc, de, x, y, z);
			int ok = 1;
			
			//在输入字符串中分别查找刚刚保存在buf字符串的每一个字符
			for(int i = 0; i < strlen(buf); i++)
				if(strchr(s, buf[i]) == NULL)
					ok = 0;
				if(ok)
				{
					printf("<%d>\n", ++ cnt);
					printf("%5d\nX%4d\n-----\n%5d\n%4d\n-----\n%5d\n\n",abc, de, x, y, z);
				}			
		}
	}
	printf("The number of solutions = %d\n", cnt);
	return 0;
}

//.cpp
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

int main()
{
	int cnt = 0;
	char s[20],buf[90];
	cin >> s;
	for(int abc = 111; abc < 999; abc++)
	{
		for(int de = 11; de < 99; de++)
		{
			int x = abc*(de%10);
			int y = abc*(de/10);
			int z = abc*de;
			sprintf(buf, "%d%d%d%d%d", abc, de, x, y, z);
			int ok = 1;
			for(int i = 0;i < strlen(buf); i++)
				if(strchr(s, buf[i]) == NULL)
					ok = 0;
			if(ok)
			{
				cout << "<" << ++cnt << ">" << endl;
				cout << setw(5) << abc << endl << "X" << setw(4) << de << endl << "-----" << endl << setw(5) << x << endl << setw(4) << y << endl << "-----" << endl << setw(5) << z << endl <<endl;
				
			}
		}
	}
		cout << "The number of solutions = " << cnt << endl;
	return 0;
}

// Example3-5TeX中的引号.cpp  
  
/** 
 * 题目名称:TeX中的引号
 * 题目描述: 
 *      在TeX中,左引号是“"”,右引号是”"“。输入一篇包含双引号的文章,你的任务是把它转成TeX格式。
 * 样例输入: "To be or not to be," quoth the Bard, "that is the question".
 * 样例输出: <To be or not to be,> quoth the Bard, <that is the question>.
 **/  
  
 /** 
  * 题目分析: 
  * 本题目关键在于如何判断一个双引号是左引号还是右引号,方法很简单,使用一个标志变量。
  * 但是本题需要解决另外一个问题,那就是输入字符串,我们首先考虑使用scanf("%s"),这里不能使用,因为它碰到空格或者TAB就会停下来。
  * 所以从标准输入读取一个字符,可以用getchar,它等价于fgetc(stdin),将读取下一个字符。
  * 本题的特点在于可以边读边处理,而不需要把输入字符串完整地存下来,因为getchar是一个不错的选择。
  * 学习笔记:
  * ①scanf("%s")碰到空格或者TAB就会停下来。
  * ②从标准输入读取一个字符,可以用getchar,它等价于fgetc(stdin),将读取下一个字符。
  * ③表达式“a?b:c”含义,这其实是if语句的表达式版
  **/  

//.c
#include <stdio.h>
int main()
{
	int c,q = 1;
	while((c = getchar()) != EOF)
	{
		if(c == "")
		{
			printf("%s", q ? "<" : ">");
			q = !q;
		}
		else printf("%c", c);
	}
	return 0;
}

// Example3-6 WERTYU.cpp  
  
/** 
 * 题目名称:WERTYU
 * 题目描述: 
 *      把手放在键盘上时,稍不注意就会往右错一位,这样,输入Q会变成输入W,输入J会变成输入K等。输入一个错位后敲出的字符串(所有的字母均大写),
 * 输出打字员本来想打出的句子,输入保证合法,即一定是错位之后的字符串。例如输入中不会出现大写字母A
 * 样例输入: O S, GOMR YPFSU/
 * 样例输出: I AM FINE TODAY.
 **/  
  
 /** 
  * 题目分析: 
  * 本题目和上面例题一样,每输入一个字符,都可以直接输出一个字符,因此getchar是输入的理想方法,问题在于如何进行输入输出变换呢?
  * 方法1:使用if语句或者switch语句,如"if(c = 'W')putchar('q')"。这样太麻烦了
  * 方法2:使用常量数组
  * 学习笔记:
  * 善用常量数组往往能简化代码,定义常量数组时无须指明大小,编译器会计算。
  * 运用常量数组时,我们常常需要查找输入字符在常量表中的位置,for(i = 1; s[i] && s[i] != c; i++)这里我们需要理解为什么是s[i] != c呢?
  * 因为我们开始是从i = 1开始的
  * 
  **/  

//.c
#include <stdio.h>
char s[] = "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
int main()
{
	int i,c;
	while((c = getchar()) != EOF)
	{
		for(i =1; s[i] && s[i] != c; i++); //找错位之后的字符在常量表中的位置
		if(s[i]) putchar(s[i - 1]);//这里注意i刚好已经指到了错位字符的位置了
		else putchar(c);
	}
	return 0;
}

// Example3-7回文词  
  
/** 
 * 题目名称:回文词
 * 题目描述: 
 *      输入一个字符串,判断它是否为回文串以及镜像串。输入字符串保证不含数字0。所谓回文串就是反转以后和原串相同,如abba和madam.所有的镜像串,就是左右镜像之后和
 * 原串相同,如2S和3AIAE。注意,并不是每个字符在镜像之后都能得到一个合法字符
 * 输入的每行包含一个字符串保证只有上述字符,不含空白字符,判断它是否为回文串和镜像串(共4种组合)。每组数据之后输出一个空行。
 * 样例输入: NOTAPALINDROME
 *            ISAPALINILAPASI
 *            2A3MEAS
 *            ATOYOTA
 * 样例输出: NOTAPALINDROME -- is not a palindrome.
 *            ISAPALINILAPASI -- is a regular palindrome.
 *            2A3MEAS -- is a mirrored string.
 *            ATOYOTA -- is a mirrored palindrome.
 *            
 **/  
  
 /** 
  * 题目分析: 
  * 本题不包含空白字符,可以安全使用scanf进行输入,回文串和镜像判断都不复杂,我们可以放一起来判断
  * 我们这道题仍然采用常量数组来完成,关键之处如何处理输入一个字符,找到镜像?这个也是本题的难点。
  * 因为我们字母和数字的镜像不一样,而且没有一定的规则,我们这里就想着把所有镜像按顺序放在一个常量数组中
  * 我们观察到一个顺序,前面26个是大写字母表,后面是1-9按大小顺序排列
  * 这里还有一难点,就是我们怎么去将回文串和镜像串放在一起判断,这里我们采用了二维字符串数组,这里很巧妙
  * 把is not a palindrome. is a regular palindrome. is a mirrored string. is a mirrored palindrome.放在一个字符串数组中
  * 再利用两个变量p m来控制输出。很巧妙  很棒
  * 学习笔记:
  * ①善用常量数组往往能简化代码,定义常量数组时无须指明大小,编译器会计算,记住定义常量数组之后往往需要找一个标记,找出输入一个字符,对应
  *   常量数组哪个字符,这里我们需要下一定的功夫,例如前面一道题目利用for(i = 1; s[i] && s[i] != c; i++)找出输入字符的位置;这道题采用与初始
  *   之间的关系来找出对应字符的位置。
  * ②学会用二维字符串数组解决一些问题
  * ③头文件ctype.h中定义的isalpha、isdigit、isprint等工具可以用来判断字符的属性,而toupper、tolower等工具可以用来转换大小写,如果ch是大写字母,
  *   则ch-'A'就是它在字母表中的序号;类似,如果ch是数字,则ch-'0'就是这个数字的数值本身
  * 
  **/ 
//.c

#include <stdio.h>
#include <string.h>
#include <ctype.h>
const char *rev = "A   3  HIL JM O   2TUVWXY51SE Z  8 ";
const char *msg[] = {"not a palindrome", "a regular palindrome","a mirrored strin", "a mirrored palindrome"};

char Reverse(char ch)
{
	if(isalpha(ch))
		return rev[ch - 'A'];
	return rev[ch - '0' + 25];
}

int main()
{
	char s[30];
	while(scanf("%s", s) == 1)
	{
		int len = strlen(s);
		int p = 1,m = 1;
		for(int i = 0; i < (len+1)/2; i++)
		{
			if(s[i] != s[len - 1 -i])
				p = 0; //不是回文串
			if(Reverse(s[i]) != s[len-i-i])
				m = 0; //不是镜像串
		}

		printf("%s -- is %s.\n\n", s, msg[m*2 + p]);
	}
	return 0;
}

// Example3-8 猜数字游戏的提示 
  
/** 
 * 题目名称:猜数字游戏的提示
 * 题目描述: 
 *      实现一个经典“猜数字”游戏,给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对(B)
 *输入包含多组数据,每组输入第一行为序列的长度n,第二行为答案序列,接下啦是猜测序列,猜测序列全0时改组数据结束,n=0时输入结束
 * 样例输入: 4
 *            1 3 5 5
 *            1 1 2 3
 *            4 3 3 5
 *            6 5 5 1
 *            6 1 3 5
 *            1 3 5 5
 *            0 0 0 0
 * 样例输出: Game 1:
 *                (1,1)
 *                (2,0)
 *                (1,2)
 *                (1,2)
 *                (4,0)
 **/  
  
 /** 
  * 题目分析: 
  * 本题主要需要做的就是
  * ①先统计A的个数,这个很简单,我们只需要建立两个数组,a[maxn] b[maxn]直接a[i] == b[i]就可以统计了;
  * ②再统计B的个数,对于每个数字(1-9)统计二者出现的次数c1和c2,之后再求一下min(c1,c2),之后这个就是数字出现对B的贡献,最后在减去A的部分,就得到答案B了
  * 学习笔记:
  * ①当我们要将一组数据和很多数据一一对比时,我们可以选择设两个a[maxn] b[maxn]之后循环一一对比
  * ②这道题我们每个数字是单个单个输入的,所以我们要有n个数,要采用循环i来scanf输入
  * ②多组猜测数据,我们这里采用for( ; ;),当然里面设置了当为0,时候直接break出来,我们要学会用这种思想
  **/ 

//.c
#include <stdio.h>
#define maxn 1010

int main()
{
	int n, a[maxn], b[maxn];
	int kase = 0;
	while(scanf("%d", &n) == 1 && n)
	{
		printf("Game %d:\n", ++kase); //输出第一句话
		for(int i=0; i < n; i++)      //输出答案序列
		{
			scanf("%d", &a[i]);
		}

		for( ; ; ) //输出猜测序列
		{
			int A = 0, B = 0;
			for(int i = 0; i < n; i++)
			{
				scanf("%d", &b[i]);
				if(a[i] == b[i])
					A++;
			}
			if(b[0] == 0) break; //正常的猜测序列不会又0,所以只需要判断第一个数是否为0即可
			for(int d = 1; d <= 9; d++)
			{
				int c1 = 0, c2 = 0; //统计数字d在答案序列和猜测序列中各出现多少次
				for(int i = 0; i < n; i++)
				{
					if(a[i] == d) c1++;
					if(b[i] == d) c2++;
				}

				if(c1 < c2)
					B += c1;
				else
					B += c2;

			}
			printf("    (%d,%d)\n", A, B-A);
		}
	}
	return 0;
}

// Example3-10 环形序列(Circular Sequence)
  
/** 
 * 题目名称:环形序列
 * 题目描述: 
 *      长度为n的环形串有n种表示法,分别为从某个位置开始顺时针得到,我们会有很多种表示,在这些表示法中,字典序最小的成为“最小表示”
 * 输入一个长度为n(n小于等于100)的环状DNA串(只包含A、C、G、T这四种字符)的一种表示法,你的任务就是输出这环状串的最小表示
 * 样例输入:CTCC的最小表示为CCCT CGAGTCAGCT 最小表示为AGCTCGAGTC
 **/  
  
 /** 
  * 题目分析: 
  * 本题提出了一个新的概念:字典序,所谓的字典序就是字符串在字典中的顺序,对于两个字符串,从第一个字符开始,当某个位置的字符不同时,该位置
  * 字符较小的串,字典序较小例如abc比bcd小;如果其中一个字符串已经没有更多字符,但另一个字符串还没结束,则较短的字符串的字典序较小例如
  * hi比history小,字典序概念推广带任意序列,例如序列1 2 4 7比1 2 5 小
  * 本题求最小表示即是需要求n个元素中的最小值一样,用变量ans表示目前为止,字典序最小串在输入串中的起始位置,之后不断更新ans
  * 学习笔记:
  * 我们要学会用一次全部枚举法,之和查表来输出这种高效的编程方法,关键在于注重这个理范围
  **/ 


//.c
#include <stdio.h>
#include <string.h>
#define maxn 105

//环形串s的表示法p是否比表示法q的字典序小
int less(const char *s, int p, int q)
{
	int n = strlen(s);
	for(int i = 0; i < n; i++)
	{
		if(s[(p+i)%n] != s[(q+i)%n])
			return s[(p+i)%n] < s[(q+i)%n];
		return 0; //相等 一个个比较,且不断的更新
	}
}

int main()
{
	char s[maxn];
	while(scanf("%s",s) == 1)
	{
       
		int ans = 0;
		int n = strlen(s);
		for(int i = 1; i < n; i++)
		{
			if(less(s, i, ans))
				ans = i;
		}
		for(int i = 0; i < n; i++)
			putchar(s[(i+ans)%n]);
		putchar('\n');
	}
	return 0;
}

                                                                                                                                                                                              To_捭阖_youth 2014.6.29. 10:47





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值