pongo题解之子序列的个数

pongo ID:enockipp

中午看到群里面在说出新题了,今天晚上本来没打算做题,本来安排是晚上在win下安装mdsplus,但是又怕像以前那样过一天就降分了,所以还是今晚做了。

首先题目要求(摘自庞果网,http://www.pongo.cn):

本题同样来自caopengcs,只要你有兴趣,每个人都可以出题(出题入口在主页右侧边栏“贡献题目”->“我要发布”内),以下是题目详情: 子序列的定义:对于一个序列a=a[1],a[2],......a[n],则非空序列a'=a[p1],a[p2]......a[pm]为a的一个子序列,其中1<=p1<p2<.....<pm<=n。 例如:4,14,2,3和14,1,2,3都为4,13,14,1,2,3的子序列。 对于给出序列a,有些子序列可能是相同的,这里只算做1个,要求输出a的不同子序列的数量。 输入: 长度为n的数组1<=n<=100,数组元素0<=a[i]<=110 输出:子序列 的个数对1000000007取余数的结果(由于答案比较大,输出Mod 1000000007的结果即可)。 函数头部: C/C++:   int run(cons int *a,int n); java   public static int run(int [] a);

我的思路是:

长度为1的字串数量就是数组中不同数字的数量;
然后找到长度为2的所有合法子串(重复的不算),并累加其数量;
长度为2的字串互不相同,所以向长度为2的子串后面添加一个数形成长度为3的字符串肯定也不相同,依次类推;

我是通过统计并累加长度为len=1...n的子串在各下标结束的数量实现的。

 

下面以 1,2,2,1,3,1,4为例进行说明

过程如下:(1)统计数组中不同的数字就是长度为1的子串,即4个;num+=4;

                   (2)统计长度len=2的合法子串(重复的只计算第一次出现就行了)在各下标处结束(如子串1,2在下标1处结束)的数量如下:

                            (紫色为子串的起始位置,其他颜色为结束点位置)

                            len_2.img

                            在下标0结束子串数量:0;

                            在下标1结束子串数量:1;

                            在下标2结束子串数量:1;

                            在下标3结束子串数量:2;

                            在下标4结束子串数量:2;

                            在下标5结束子串数量:1;

                            在下标6结束子串数量:3;

                             那么长度为2的子串数量就是1+1+2+2+1+3=10个;

                            num+=10;

                      (3) 由长度len=2的合法子串在各下标处结束的数量更新长度len=3的子串在各下标处结束的数量:

                            (由长度为len的子串构建长度为len+1的子串时,只向子串后面加入一个数字,且如果某个数字在子串后面的序列中有重复出现,那么只加入在子串后面的数字序列中的第一次出现,如数字1在子串1,2后面有2次出现,那么此时由子串1,2形成长度为3的子串时只考虑下标为3的这个数字1,这样才能构成所有的子串并且不重复)

                            下标0,1处为0;

                            下标2处=1

                            下标3处=1 + 1

                            下标4处=1 + 1 + 2

                            下标5处=0 + 0 + 2 + 2

                            下标6处=1 + 1 + 2 + 2 + 1

                                      i=  (1) (2) (3)  (4)  (5)

                             (每一列代表长度为2的子串在i处结束,扩展成长度为3的子串在各下标处结束的情况)

                             (1+1+1+1)+(1+1+1)+(2+2+2)+(2+2)+(1)=18;

                             num+=18;

                       (3)继续直到len=n。

#include <cstdio>
#include <string>
#include <cstring>
#include<iostream>
#include<set>
#include<ctime>
using namespace std;

int run(const int *a,int n){
	int sumNum=0;
	int *locateNum0=new int[n];
	int *locateNum1=new int[n];
	bool *preHasSame=new bool[n];
	std::set<int>*dig=new std::set<int>;
	int i=n-1;
	int j=0;
	for(i;i>=0;i--){
		dig->insert(a[i]);
		locateNum0[i]=locateNum1[i]=0;
	}//for
	sumNum+=dig->size();//长度为1的字串
	for(i=0;i<=n-1;i++){
		preHasSame[i]=false;
		for(j=0;j<i;j++)
			if(a[i]==a[j]){
				preHasSame[i]=true;
				break;
			}//if
	}//for
	//长度为2的子串的结束点统计
	for(i=0;i<=n-1;i++){
		if(!preHasSame[i]){
			dig->clear();
			for(j=i+1;j<=n-1;j++){
				std::pair<std::set<int>::iterator,bool> retPair=dig->insert(a[j]);
				if(retPair.second) {locateNum0[j]++;locateNum0[j]%=1000000007;}
			}//for
		}//if
		sumNum+=locateNum0[i];//加上长度为2的子串数量
		sumNum%=1000000007;
	}//for
	//长度为大于等于3的子串的结束点统计
	int len=3;
	for(len=3;len<=n;len++){
		for(i=len-2;i<=n-1;i++){
			if(locateNum0[i]){
				dig->clear();
				for(j=i+1;j<=n-1;j++){
					std::pair<std::set<int>::iterator,bool> retPair=dig->insert(a[j]);
					if(retPair.second) {locateNum1[j]+=locateNum0[i];locateNum1[j]%=1000000007;}
				}//for
			}//if
			sumNum+=locateNum1[i];//加上长度为len的子串数量
			sumNum%=1000000007;
		}//for
		int *mid=locateNum0;
		locateNum0=locateNum1;
		locateNum1=mid;
		for(int ii=0;ii<=n-1;ii++)
			locateNum1[ii]=0;
	}//for
	return sumNum;
}

//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{   
    int * a= new int [7];
    a[0]=1;
    a[1]=2;
    a[2]=2;
    a[3]=1;
    a[4]=3;
    a[5]=1;
    a[6]=4;
    std::cout<<run(a,7)<<std::endl;
    return EXIT_SUCCESS;

} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。


 

 

 

                            

                    

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值