我该如何解决这个问题以提高效率?
Posted
技术标签:
【中文标题】我该如何解决这个问题以提高效率?【英文标题】:How do I solve this making it more efficient? 【发布时间】:2015-10-11 09:29:36 【问题描述】:所以,我正在尝试解决以下问题:https://www.codechef.com/TSTAM15/problems/ACM14AM3
火星轨道飞行器任务探测器从位于安得拉的 Satish Dhawan 航天中心 (Sriharikota Range SHAR) 的第一个发射台升空 邦,使用极地卫星运载火箭 (PSLV) 火箭 C25 在 2013 年 11 月 5 日 09:08 UTC (14:38 IST)。
这次成功发射的秘诀在于 ISRO 的发射台 用过的。发射台的一个重要组成部分是发射塔。这是 支撑火箭的长垂直结构。
ISRO 现在希望为他们的下一个任务建造一个更好的发射台。 为此,ISRO 获得了一根长钢筋,发射塔可以 通过从条上切割一段来制成。作为节省成本的一部分, 他们获得的酒吧不是同质的。
条由几个块组成,其中第 i 个块有 持久性 S[i],它是一个介于 0 和 9 之间的数字。一个段是 定义为一个或多个块的任何连续组。
如果他们从 ith 块到 jth 块中切出一段条形 (i(S[i]*10(j-i) + S[i+1]*10(j-i-1) + S[i+2]*10(j-i-2) + … + S[j] * 10(0)) % M 给出。换句话说,如果
W(i,j)
是由以下组成的以 10 为底的数字 连接数字S[i]
、S[i+1]
、S[i+2]
、...、S[j]
,然后 段(i,j)
的持久性是W(i,j) % M
。由于 ISRO 不会透露的技术原因, 用于建造发射塔的部分应该正好是 L。 给定 S 和 M,找出 ISRO 可以从 耐久度为L的钢筋。输入
第一行包含一个字符串 S。这个字符串的第 i 个字符 表示第 i 段的持久性。下一行包含一个 单个整数 Q,表示查询次数。下一个Q中的每一个 行包含两个空格分隔的整数,分别表示 M 和 L。输出
对于每个查询,输出切割条的方式数 单独的线。约束
1 ≤ |S| ≤ 2 * 10^4 Q ≤ 5 0 < M < 500 0 ≤ L < M
示例
输入:
23128765 3 7 2 9 3 15 5
输出:
9 4 5
说明
对于
M=9
、L=3
,除以余数为3的子串 9 是:3、31287、12 和 876。
现在,我所做的是,我最初生成给定长度的数字的所有可能子字符串,并尝试将其除以给定数字以检查它是否可整除并将其添加到答案中。因此,我的代码是,
string s;
cin>>s;
int m,l,ans=0;
for ( i = 0; i < s.length(); i++ )
for ( j = i+1; j < s.length(); j++ )
string p = s.substr(i,j);
long long num = stoi(p);
if (num%m == l)
ans++;
cout<<ans<<"\n";
return 0;
但显然由于输入长度最多为 10^4,这在所需时间内不起作用。我怎样才能使它更优化?
【问题讨论】:
这不是完整的代码,因为i
和 j
没有声明。请像人们通常那样定义循环控制变量:for(int i = 0;...
.
stoi
仅适用于短数字,但您可能必须考虑字符串的所有子字符串,最多可达 2000 位。您需要在较低级别应用模运算。
咦,ISRO用钢筋做发射塔? -_-
【参考方案1】:
我可以给你的一个小建议是将一个变量初始化为s.length()
,以避免每次为每个for
块调用该函数。
【讨论】:
这是一个微优化。这是微不足道的。 正如我所说,这只是一个小建议,而不是大的代码编辑。 C++ 字符串的length()
成员只是一个访问器;它没有与 C 的strlen
相同的速度损失。这种微优化可能会使代码更快一点,但我认为真正的改进在于使用不同的算法。【参考方案2】:
好的,这里有一个工作程序在底部
主要优化#1
当涉及到整数运算时,不要(永远)使用字符串。您一遍又一遍地转换字符串 => 整数(这是一个 O(n^2) 问题),这非常慢。此外,它也没有抓住重点。
解决方案:首先将您的字符数组(字符串)转换为数字数组。整数运算很快。
主要优化#2
使用从“子字符串”到数字的智能转换。将字符转换为实际整数后,它们成为多项式a_n * 10^n
中的因子。要将 n 个段的子字符串转换为数字,只需计算 sum(a_i * 10^i)
即可得到 0 <= i < n
。
非常好,如果系数 a_i 按照问题陈述中的方式排列,您可以使用霍纳的方法 (https://en.wikipedia.org/wiki/Horner%27s_method)非常快速评估子字符串的数值。
简而言之:保持当前子字符串的运行值并将其增长一个元素就是* 10 + new element
示例:字符串“128472373”。
第一个子字符串 = "1",值 = 1。 对于第二个子字符串,我们需要 添加数字“2”如下:value = value * 10 + "2"
,因此:value = 1 * 10 + 2 = 12
。
对于第三个子字符串需要添加数字“8”:value = value * 10 + "8"
,因此:value = 12 * 10 + 8 = 128
。
等等。
我在格式化 C++ 代码内联时遇到了一些问题,所以我将其卡在 IDEone 中:https://ideone.com/TbJiqK
节目要点:
在主循环中,遍历所有可能的起点:
// For all startpoints in the segments array ...
for(int* f=segments; f<segments+n_segments; f++)
// add up the substrings that fullfill the question
n += count_segments(f, segments+n_segments, m, l);
// Output the answer for this question
cout << n << endl;
count_segments()
函数的实现:
// Find all substrings that % m == l
// Use Horner's algorithm to quickly evaluate sum(a_n*10^n) where
// a_n are the segments' durabilities
int count_segments(int* first, int* last, int m, int l)
int n = 0, number = 0;
while( first<last )
number = number * 10 + *first; // This is Horner's method
if( (number % m)==l )
n++;
// If you don't believe - enable this line of output and
// see the numbers matching the combinations of the
//cout << "[" << m << ", " << l << "]: " << number << endl;
first++;
return n;
【讨论】:
以上是关于我该如何解决这个问题以提高效率?的主要内容,如果未能解决你的问题,请参考以下文章