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处结束)的数量如下:
(紫色为子串的起始位置,其他颜色为结束点位置)
在下标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 //提示:自动阅卷结束唯一标识,请勿删除或增加。