poj1850

题目不难,大致题意为将26个小写字母(可以小于26)所组成的所有满足题意要求的字符串按照题目要求编码。要求该字符串由小写字母组成,并且是严格升序排列。将所有这样的字符串编码,编码规则如下:

1)按照长度递增编码

2)长度相同时,按照字符串的大小编码

3)字符串的码值即为编码序号

题目要求给出一个串,若是符合题意要求的字符串则求出它的编码值,否则直接输出0。

题意很明确。就是要寻找编码值。关键在于如何求出编码值,分析如下:

可以分成两部分来求编码值:

1)将所有比该字符串长度小的最大编码求出来

2)求解该长度下,该字符串的编号

对于上述两个问题,核心就是如何求解一个长度下的字符串个数。只要把这个弄懂了,就OK了!

对于一个长度下的字符串个数,无非就是从26个字母中取出该长度个数的字符组成字符串,并且是递增字符串的不同个数。这里介绍两种方法:递推法、穷举法(深搜实现)

1)递推法

手算长度为1的个数为26

        长度为2的个数为∑26-i(1<=i<=25)

        长度为3的个数为∑i∑j (26-j)  (2<=i<=25 && i<=j<=25)

        长度为4的个数为∑i∑j∑k (26-k) (3<=i<=25 && i<=j<=25 && j<=k<=25)

        ……

        长度为n的个数为∑∑∑……∑(n-1次求和)(26-n(n-1)) (n-1<=n1<=25 && n1<=n2<=25 && …… )

这个递推式可以很容易的用深搜实现,这里不再多讲,具体看程序。

2) 穷举法

直接用深搜穷举该长度下的所有字符串个数(看程序)

注意穷举法和递推法虽然时间复杂度相差不大,但是前者的递归深度要小一些,所需系统堆栈要少。故所需时间要相对较少。

下面分别是递推法和穷举法的AC时间 :157MS  ,  250MS

下面是代码:(穷举和递推) 132K+157MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 20
char Input[Max]; // 输入
char record[Max]; // 辅助数组
int Sum,len,lendif; 
/*void dfs_get_between(int pivot,int index){ // 递推求解
	if(pivot==0){
		Sum+=26;
		return ;
	}
	if(pivot==1){
		for(int i=index;i<=25;i++)
			Sum+=(26-i);
		return ;
	}
	else{
		for(int i=index;i<=25;i++)
			dfs_get_between(pivot-1,i);
	}
}*/		
bool dfs_get_num(int pivot,int Inf){ // 深搜求解一个长度下该字符串的编码序号
	if(pivot==len+1){
		record[len]='\0';
		Sum++;
		if(strcmp(record,Input)==0) // 注意若查找到原串,则直接返回,由于要连带返回,故设计成bool
			return true;
		return false;
	}
// 算法核心,剪枝关键在于:将每个字符的可选范围由(a-z)压缩成比前一个字符大,且同时要保证后面字符有可选字符余地。这样可以保证每个字符串都为递增,且每个字符的枚举顺序都是从小到大,这样也可保证所得同一长度下的字符串均是按升序排列
	for(int i=Inf+1;i<=26-(len-pivot);i++){  
		record[pivot-1]=i+'a'-1;
		if(dfs_get_num(pivot+1,i))
			return true;
	}
	return false;  // 注意回溯
}
void dfs_get_dif(int pivot,int Inf){ // 区别于上面深搜是无需返回,求所有的字符串个数
	if(pivot==lendif+1){
		Sum++;
		return ;
	}
	for(int i=Inf+1;i<=26-(lendif-pivot);i++)
		dfs_get_dif(pivot+1,i);
}

bool Is_right(){ // 判断输入的字符串是否合法
	for(int i=0;i<len-1;i++)
		if(Input[i]<'a' || Input[i]>'z' || Input[i]>=Input[i+1])
			return false;
	if(Input[len-1]<'a' || Input[len-1]>'z')
		return false;
	return true;
}
int main(){
	scanf("%s",Input);
	len=strlen(Input);
	if(!Is_right()){
		printf("0\n");
		return 0;
	}
	Sum=0; //初始化为0
	for(int i=1;i<=len-1;i++){ //求解比该长度小的最大编码,两种方法均可
		//dfs_get_between(i-1,i-1);
	    lendif=i;
		dfs_get_dif(1,0);
	}
	dfs_get_num(1,0); //求解该长度下的编码序号
	printf("%d\n",Sum);
	return 0;
}



 

 

  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值