新年之初,登陆CSDN看到了英雄会的题目,好久没有头脑风暴了,一看题目还算掌握中,决定试试,于是开始分析题目了。
题目要求: 例如有一个字符串"iinbinbing",截取不同位置的字符‘b’、‘i’、‘n’、‘g’组合成单词"bing"。若从1开始计数的话,则‘b’ ‘i’ ‘n’ ‘g’这4个字母出现的位置分别为(4,5,6,10) (4,5,9,10),(4,8,9,10)和(7,8,9,10),故总共可以组合成4个单词”bing“。
问题是:现给定任意字符串,只包含小写‘b’ ‘i’ ‘n’ ‘g’这4种字母,请问一共能组合成多少个单词bing?
字符串长度不超过10000,由于结果可能比较大,请输出对10^9 + 7取余数之后的结果。
分析了一下,可以开创四个数组,分别记录b,i,n,g的位置,通过位置进行比较求解。
先定义四个vector<int> 对象:
vector<int> b;
vector<int> i;
vector<int> n;
vector<int> g;
对于任意一个输入字符串input,先进行如下操作,
int len = input.length();
for(int j = 0;j<len;j++)
{
x = input[j] - 'a';
switch(x)
{
case 1: //‘b'
b.push_back(j);
break;
case 8: // 'i'
i.push_back(j);
break;
case 13: //'n'
n.push_back(j);
break;
case 6: //'g'
g.push_back(j);
}
}
如给定输入input = "iinbinbing";
b[] = { 3,6 }
i[] = { 0,1,4,7 }
n[] = { 2,5,8 }
g[] = { 9 }
算法一:
通过嵌套循环遍历 b[],i[],n[],g[],当数组中存放的值为递增序时,结果+1,复杂度O(n^4),代码略。
程序复杂度分析,由于输入长度最多为10000,因此所有测试数据中,复杂度最大的为2500^4,即是
bbb.........iii............nnn............ggg.........
(各2500个)
而对于2500^4 = 39062500000000 , 是一个非常庞大的数据。因此该算法必然导致超时。
算法二:
增加定义四个数组:
vector<long long> result_b;
vector<long long> result_i;
vector<long long> result_n;
vector<long long> result_g;
每个result_x数组中的值分别表示对应x数组中值所包含的可行解,result_x初始化为:
int len_b = b.size();
result_b.assign(len_b,0);
int len_i = i.size();
result_i.assign(len_i,0);
int len_n = n.size();
result_n.assign(len_n,0);
int len_g = g.size();
result_g.assign(len_g,1);
如:
b[] = { 3,6 }
i[] = { 0,1,4,7 }
n[] = { 2,5,8 }
g[] = { 9 }
====>
result_g[] = {1} , 可行解就是本身
result_n[] = {1,1,1} , 对于n[0]=2来说,g[]中有1个值(9)比2大,因此result_n[0] = result_g[0]
result_i[] = {3,3,2,1}, 同理,对于i[2]=4来说,n[]中有2个值(5,8)比4大,因此result_i[2] = result_n[1]+result_n[8]=2
result_b[] = {3,1} , 对于b[0] = 3来说,i[]中有两个值(4,7)比3大,因此result_b[0] = result_i[2] + result_i[3] = 3
最后对result_b[]求和即是最后的结果。
因此把上面的四重循环分解为三个二重循环,第一次n[]和g[]循环,第二次i[]和n[]循环,第三次b[]和i[]循环。复杂度为O(n^2),最坏数据为biii.......nnn.......g,其中有连续两个字符为4999个,最坏计算次数约为5000^2 = 25000000,按理说在10^8下应该很快运行结束,按照该算法进行挑战,提交后发现还是超时。
利用程序构造字符串测试了一下,大概得花10秒左右,想到可能是测试数据太多在vector要进行不断的创建造成,精简了一些if语句,比如在第一个二重循环时,数据不可能大于10^9,省略了该步骤的条件判断,再次提交,还是超时。
由于已经挑战失败,只能当学习再试试改进。
算法三:
在算法二的基础上,增加了标记,分析在求解result_x[]数组时,result_x最后的结构必然是一个非递减序列,因此求result_x[i]的值均可以用到result_x[i-1]进行快速求解,省去了对第二层数组的累加。
比如在上例:
b[] = { 3,6 }
i[] = { 0,1,4,7 }
再求得result_i[] = {3,3,2,1}时,求result_b[]时,最多只需要遍历i[]2次即可。
第一次计算sum(result_i[]) = 9;
第二次计算result_b[] 具体如下:
定义一个位置site初始指向i[0],若b[0] > i[site],num -= result_i[site]且site++,直到b[0]<i[site],result_b[0] = num
代码实现为
num = sum(result_i);
site = 0;
for(int j = 0;j<len_b;j++)
{
while(site<len_i && b[j]>i[site])
{
num -= result_i[site];
site++;
}
result_b[j] = num;
}
这样对于b[] ,i[] 相当于各自只需要遍历一次,算法复杂度O(n)。
最后只需要计算 c = sum(result_b); 即可。
由于结果较大,需要用大整数,但是分析了最大数据小于long long类型(64位),因此定义计数c为long long类型,最后取模即可,最后提交顺利通过。
源代码下载地址 :http://download.csdn.net/detail/longteng1116/6801531