【编程之美】中的美中不足,谈谈我的理解

拿到书本后,看了一下,里面的题目并不是太难,都是初级算法,或者有些根本就不用到算法。

在二分查找的知识点中,还是比较新颖的,看来我写的二分一直存在bug啊。在看看其他方面的题目,很多题目还是要总结分享一下的,

第三章的题目全部看完了,自己也写了一下程序进行了优化。部分作者的程序没有看~cpp的变量名太长了,又不是写项目啊~自己添加的名字估计自己以后看比较方便,对于读者来说,估计作用并不是太大。不如写成伪算法,这样更好一点啊。对吧~~~~~~

话说这个完全可以用二进制编程来处理。具体的二进制编程技巧,谷歌搜索下就知道了。只是让别人看到的时候不容易理解。大牛必备。我也是运用很差的一种,组合题目还是可以搞定的。

解法1.下面是做的个字符串包含问题。记得以前竞赛中出现一个计算机二进制位中的循环数,不过是每8次一个循环,看是不是最后2个int数为循环数。解题思路一样。

解法2:.估计大牛就说了,不如写一个循环队列吧。这样指针到最后重新回来对比就好了。循环一次需要统计打一个标兵记录下。第二次末尾就停止。只是对于解法一,使用的空间少了点。

/*
编程之美-字符串移动包含问题
解题:
将s1与s2取出较大的串扩增一倍,然后用较短与其扩增后的字符串循环对比即可 

AABCD
CDAA

true

ABCD
ACBD

false
*/
#include <iostream>
#define bug printf("bug");
using namespace std;
const int MAXN=1<<7;
char s1[MAXN],s2[MAXN],tmp[MAXN];
int l1,l2,t;

void input(){
	scanf("%s",s1);
	scanf("%s",s2);
	l1=strlen(s1);
	l2=strlen(s2);
	//printf("%c  %c",s1,l1);
}

int strcmp(char* s1,char* s2){
	int short_s=strlen(s2);
	for(int i=0;i<((t<<1)-1);i++){
		if(s1[i]==s2[0]){
			int j=1;
			for(;j<short_s;j++){
				if(s1[i+j]!=s2[j]){
					break;
				}
			}
			if(j==short_s)
				return 1;
		}
	}
	return -1;
}
void sovle(){

	t=l1>=l2?l1:l2;
	int flag=-1;
	if(t==l1){  //l1>=l2
	 for(int i=0;i<l1;i++)
	 	s1[i+l1]=s1[i];
	 	 flag=strcmp(s1,s2);
	}else{
		for(int i=0;i<l2;i++)
	 	s2[i+l1]=s2[i];
	 	flag=strcmp(s2,s1);
	}
	if(flag==-1)
	printf("false\n");
	else
	printf("true\n");
}

int main(){
	input();
	sovle();
	return 0;
}

这个题目是指一串电话号(每个数字对应一组字符,老版诺基亚按键~),翻译出组合的单词数量

然后将单词的数量通过英语字典对别输出。这一步联网下载字典就行了。

本题写第一步。

ps:作者的程序啊啊啊啊,难于理解,估计我看了半天才明白过来。都加注释了。

本想用dfs的路径输出下来:

存路径,很多很多~~~每个数字对应的字符数的极限和

不存路径,那么就是在叶子节点输出所有叶子组合,然后回退到父节点,然后输出组合。直到根部节点,这样就是所谓的作者的解题思路、也不是什么dfs了。。。

/*

8
2 6 6 7 8 8 3 7

3
2 6 6

*/
#include<iostream>
using namespace std;
const int MAXN=1<<7;
int n;
char c[10][10]={
	"",
	"",
	"ABC",
	"DEF",
	"GHI",
	"JKL",
	"MNO",
	"PQRS",//7
	"TUV",
	"WXYZ"//9
};
int total[10]={0,0,3,3,3,3,3,4,3,4};
int ans[MAXN];  //代表c[][j]的位置
int num[MAXN];
void input(){
	scanf("%d",&n);
	int i=0;
	while(i<n)scanf("%d",&num[i++]);
}

/*

复杂度:
每个数字占用字符数量的乘积 

循环的操作方向为:向右
递归的操作方向为:向下
共同操作的产生全部组合
递归到终点,输出每个组合代表的数字即可
*/
void sovle2(int* num,int* ans,int i,int n ){
	if(i==n){  //递归变脸到第i个数
		for(int j =0;j<n;j++) //打印递归到的最后一行输出组合
		printf("%c", c[num[j]][ans[j]]);
		printf("\n");
	}
	for(ans[i] = 0; ans[i] < total[num[i]];ans[i]++)  //从第i个数字向右循环每次递归输出
		sovle2(num, ans, i + 1, n);
}
void sovle1(){
	while(true){
		//打印n位数字对应的n个字符串中首位字符
		for(int i = 0; i < n; ++ i)
		printf("%c", c[num[i]][ans[i]]);
		printf("\n");
		//system("pause");
		int k = n - 1;	//最后一个号码位置
		while(k >= 0){//n-1~0  从下到上
			if(ans[k] < total[num[k]] - 1){  //j<max j-1
				ans[k]++;    // 最后一个号码位置c[i][ans[k]]第二个参数增加至~本字符的最后一位 ,跳出循环打印
				break;
			}else{
			ans[k] = 0;  // k行已经完成输出,回退一行
			--k;
			}
  		}
		if(k < 0) break;  //k从n-1~0完成输出停止外层循环
	}
}

int main(){
	input();
	//sovle1();
	sovle2(num,ans,0,n);
	return 0;
}
/*
2个字符串的相似度问题: 

对2个字符串有三种操作,使其相等的最小操作步骤。注意:是最小~,如果次数为x,那么定义为距离,然后相似度就是1/(x+1)
作者书中木有提到,估计让人费解了~~~~不过程序中写的意思就是啦 

1.修改其中一个字符串的字符
2.增加
3.删除

算法分析:
1.相同的字符,比较下一位(因为下一位之后的字符串的操作步骤不会比操作本次的步骤多,这是一个贪心选择问题的意味~)
2.不相同的字符进行三种操作,是操作a呢还是操作b,所以有6种选择。这样想其实完全没有必要的。考虑操作后的状态变化就行了。
无论什么操作,最后结果状态一定是三种其中的一个。
	1.A=2~lenA B=1~lenB
 	2.A=1~lenA B=2~lenA
 	2.A=2~lenA B=2~lenB
所以对于三个操作后的状态进行递归求解,取三种选择的最小值就ok了。
看代码~~~~~~~ 

a b
abdd aebdd
travelling traveling
*/
#include<iostream>
using namespace std;
char a[100],b[100];
int lenA,lenB;
void input(){

	scanf("%s",a);
	scanf("%s",b);
	lenA=strlen(a);
	lenB=strlen(b);
	//printf("%s %s",a,b);
}

int calStrDis(char* a,int begin_a,int end_a,char* b,int begin_b,int end_b ){
	//边界检测
	if(begin_a>end_a){  //不满足条件A
		if(begin_b>end_b)
		return 0;
		else return end_b-begin_b+1;  //返回修改的字母B的长度,说明A,B没有交集
	}
	if(begin_b>end_b){
			if(begin_a>end_b) return 0;
			else 	return end_b-begin_a+1;
	}
	if(a[begin_a]==b[begin_b]){  //相等返回下一位~n字符串计算
		return calStrDis(a,begin_a+1,end_a,b,begin_b+1,end_b);
	}else{
		//三种操作结果
		int t1=calStrDis(a,begin_a+1,end_a,b,begin_b,end_b);
		int t2=calStrDis(a,begin_a,end_a,b,begin_b+1,end_b);
		int t3=calStrDis(a,begin_a+1,end_a,b,begin_b+1,end_b);
		return min(t1,min(t2,t3))+1;
	}	 

}

void sovle(){
	printf("%.2f\n",1/(1+(double)calStrDis(a,0,lenA-1,b,0,lenB-1)));
}

int main(){
	input();
	sovle();
 	return 0;
}
/*
有先跟序列和中跟序列
输出后跟序列 

解法:
先跟访问到的是跟,记录位置p
然后递归构造左右子树
将本次跟放到辅助数组s的最后

输出辅助数组s 

DBACEGF ABCDEFG

ACBFGED
*/
#include<iostream>
using namespace std;
const int MAXN=1<<7;
char s1[MAXN],s2[MAXN],s[MAXN];
void input(){
	scanf("%s%s",s1,s2);
}

void build(int n,char* s1,char* s2,char* s){
	if(n<=0) return ;
	int p=strchr(s2,s1[0])-s2;  //划分左右子树位置
	build(p,s1+1,s2,s);     //左子树,p长度从s1+1,s2+1+p构建,存储在s中
	build(n-1-p,s1+1+p,s2+1+p,s+p); //构建右子树,n-1-p的长度,s1+1+p,s2+1+p开始构造,存储s+p中
	s[n-1]=s1[0];
}
void sovle(){
	int n=strlen(s1);
	build(n,s1,s2,s);
	printf("%s\n",s);
}

int main(){
	input();
	sovle();
	return 0;
}

= =有米有错的啊啊啊a~~~~

时间: 2024-11-05 23:23:23

【编程之美】中的美中不足,谈谈我的理解的相关文章

编程之美中1的个数的问题

//采用最直接的方法,时间复杂度为O(N*logN): /*#include <iostream> #include<typeinfo> #include<specstrings.h> #include<string> using namespace std; int num_one(int n) { int count=0; while(n) { count+=(n%10==1)?1:0; n/=10; } return count; } int core

编程之美中判断某个字符串是否是另一个字符串的移位结构的子串

#include<iostream> #include<string> using namespace std; bool is_circle(string src,string des) { if(src.empty()||des.empty()) return false; string temp=src; temp+=src; string::size_type pos=0; pos=temp.find(des); if(pos==string::npos) return f

编程之美之买票找零

题目:假设有2N个人在排队买票,其中有N个人手持50元的钞票,另外有N个人手持100元的钞票,假设开始售票时,售票处没有零钱,问这2N个人有多少种排队方式,不至使售票处出现找不开钱的局面? 分析:队伍的序号标为0,1,...,2n-1,并把50元看作左括号,100元看作右括号,合法序列即括号能完成配对的序列.对于一个合法的序列,第0个一定是左括号,它必然与某个右括号配对,记其位置为k.那么从1到k-1.k+1到2n-1也分别是两个合法序列.那么,k必然是奇数(1到k-1一共有偶数个),设k=2i

编程之美之实时排名算法

首先来看一个实时排名算法 参考文献 某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新.现在要为该网站设计一种算法,在每次用户登录时显示其当前积分排名.用户最大规模为2亿:积分为非负整数,且小于100万. 存储结构 首先,我们用一张用户积分表user_score来保存用户的积分信息. 表结构: 示例数据: 下面的算法会基于这个基本的表结构来进行. 算法1:简单SQL查询 首先,我们很容易想到用一条简单的SQL语句查询出积分大于该用户积分的用户数量: select 1 + count(t

编程之美之最短摘要生成

书上给出了最短摘要的描述即算法,简单来说就是: 扫描过程始终保持一个[pBegin,pEnd]的range,初始化确保[pBegin,pEnd]的range里包含所有关键字 .然后每次迭代,尝试调整pBegin和pEnd: 1.pBegin递增,直到range无法包含所有关键字 2.pEnd递增,直到range重新包含所有关键字 计算新的range,与旧的range相比,看是否缩短了,如果是,则更新 不考虑关键字的先后顺序 .这里给出最短摘要算法的几个应用,首先是leetcode上面的两题: M

java并发编程之美-阅读记录1

1.1什么是线程? 在理解线程之前先要明白什么是进程,因为线程是进程中的一个实体.(线程是不会独立存在的) 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程中的一个执行路径,一个进程中至少会有一个线程,进程中的多个线程共享进程的资源. 线程:是cpu分配的基本单位. 由上图可看出,一个进程中会有多个线程,多个线程共享堆和方法区,但是每一个线程都会有自己的栈和程序计数器. 为什么要将栈和程序计数器设置为线程私有的呢? 前边说线程是cpu执行的基本单位,而cp

编程之美---最大公约数

该文出自于编程之美中关于最大公约数问题一章. 任意给定两个数字,得到其最大公约数 GCD(greatest common divisor),如果两个数字都很大怎么解决. 分析:最大公约数早在公元前300年,欧几里得的<几何原本>里就提出了一个高效率算法---辗转相除法. 解法一: 假设f(x,y)表示x,y的最大公约数,取k=x/y,b=x%y,则x=ky+b,如果一个数字能同时整除x,y,那么必能够同时整除b,y:而能够同时整除b,y的数必能够同时整除x,y,即x,y的公约数与b,y的公约数

求二进制数中1的个数(编程之美)

求二进制数中1的个数 继京东618店庆时买的<编程之美>这本书,翻了翻,发现里面的题还是挺有意思的,看起来我们觉得很简单的题目,解法却有很多很多种,真是一个比一个巧妙,于是,决定记录一下. 书中的题目如下 对于一个字节(8bit)的无符号数,求其二进制表示中"1"的个数,要求算法的执行效率尽可能高. 就像书中给我们说的一样,我们一般人可能想到的解决方法如下 int countOne(int n){ int count=0; while(n){ if(n%2==1){ cou

编程之美——二进制数中1的个数

解法一:若二进制末尾为1,则除以2余1: int count(int a) { int num=0; while(a) { if(a%2==1) ++num; a=a/2; } return num; } 解法二:使用移位操作相除: int count(int a) { int num=0; while(a) { num+=a&0x01; a>>=1; } return num; } 解法三:以上两种算法复杂度log2(t),t为a的二进制表示位数,若要求复杂度仅与二进制表示中1的个数