我该如何解决这个问题以提高效率?

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=9L=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,这在所需时间内不起作用。我怎样才能使它更优化?

【问题讨论】:

这不是完整的代码,因为 ij 没有声明。请像人们通常那样定义循环控制变量: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 &lt;= i &lt; 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;

【讨论】:

以上是关于我该如何解决这个问题以提高效率?的主要内容,如果未能解决你的问题,请参考以下文章

如何改进此 Python 代码以提高效率?

通过正确定义索引列表来提高代码效率

如何提高ABAP程序运行效率

个人实践:总结方法论,提高做事效率

智邦国际ERP如何提高生产订单管理效率

如何提高团队工作效率