查找长度大到10,000的字符串的子序列

时间:2012-08-03 17:26:05

标签: c++ string permutation

我有一个字符串,其大小可以大到“10,000”。我必须计算那些可被9整除的后缀。

SUBSEQUENCE:子序列是一种安排,其中维持给定字符串的顺序。例如:如果给定的字符串是10292,那么它的一些子序列是1,102,10,19,12,12(12是两次2来两次),129,029,09,092等。有些数字不是给定字符串的子序列是:201(2和0不能在1之前),921,0291等。

我试图使用位移生成给定字符串的所有子序列(powerset)并检查每个字符串是否可被9整除。但只要字符串的长度为< = 10,这就可以正常工作。在那之后,我没有得到适当的子序列(一些子序列显示负数)。

以下是我的代码:

    scanf("%s", &str); //input string 

    int n=strlen(str); //find length of string

    //loop to generate subsequences
    for(i=1;i<(1<<n);++i){

        string subseq;

        for(j=0;j<n;++j){

            if(i&(1<<j)){

                subseq+=str[j]; // generate subsequence
            }
        }

        //convert generated subseq to int; number is 'long' tpye
        number=atol(subseq.c_str());printf("%ld\n", number); 

        //ignore 0 and check if number divisible by 9
        if(number!=0&&number%9==0)count++;
    }

        printf("%ld\n", count);

4 个答案:

答案 0 :(得分:4)

由于数字可以被9整除,当且仅当其数字的总和可以被9整除时,您可以使用O(n)递归算法来解决这个问题。

这个想法如下:在每个步骤中,将子序列分成两部分并确定(递归地)有多少个序列的数字总和为i % 9,其中i的范围为{{1}到0。然后,通过以下方式“合并”8中的两个表,为整个范围构建这个相同的表。假设O(1)是左侧分割的表格,L是右侧分割的表格,您需要为整个范围构建表格R

然后你有:

F

只有一个数字for (i = 0; i < 9; i++) { F[i] = L[i] + R[i]; for (j = 0; j < 9; j++) { if (j <= i) F[i] += L[j] * R[i - j] else F[i] += L[j] * R[9 + i - j] } } 的子序列的基本情况很明显:只需将d和所有其他条目设置为零。

完整的C ++ 11实现:

F[d % 9] = 1

答案 1 :(得分:4)

我有个主意!

由于您只需计算子字符串,因此您并不关心他们实际 的内容。因此,您只需存储可能总和的数量。

然后,如果你有一个可以组合两个子串集的计数表的函数,并给出它们的组合计数怎么办?

既然我知道这是一个可怕的解释,我会举一个例子。说你给了这个号码:

2493

将它分成两半并继续分裂,直到你得到个别数字:

   2493
   /  \
 24    93
 /\    /\
2  4  9  3

2总结为什么?简单:2。而4只能汇总到4。您可以构建有多少子串总和到每个值(mod 9)的表:

   0 1 2 3 4 5 6 7 8
2: 0 0 1 0 0 0 0 0 0
4: 0 0 0 0 1 0 0 0 0
9: 1 0 0 0 0 0 0 0 0
3: 0 0 0 1 0 0 0 0 0

组合两个表很容易。添加第一个表,第二个表以及两个mod 9的每个组合(对于第一个组合,这相当于2424;对于第二个, 9393):

    0 1 2 3 4 5 6 7 8
24: 0 0 1 0 1 0 1 0 0
93: 1 0 0 2 0 0 0 0 0

然后再做一次:

      0 1 2 3 4 5 6 7 8
2493: 3 0 2 2 2 2 2 2 0

您的答案就在0栏中3。这对应于子字符串24324939。但是,你不知道,因为你只存储了计数 - 幸运的是,你不在乎!

一旦实施,这将为您提供O(n)表现 - 您只需要弄清楚如何合并O(1)中的表格。但是嘿 - 做作业吧?祝你好运!

答案 2 :(得分:0)

如果您使用 int ,那么您不应该将其移位太多。如果这样做,则设置符号位。使用 unsigned int 。或者不要左移太多。如果你坚持 int ,你可以在完成后进行右移。

用于

printf("%ld\n", count); 

printf在显示long-int类型时可能会遇到问题。你尝试过cout吗?

答案 3 :(得分:0)

这是根据Akappa算法的C ++代码。然而,该算法对于包含一个或多个0的数字是失败的,即在“10292”和“0189”的情况下,但给出“1292”和“189”的正确答案。如果有人可以调试这个来为所有案例提供答案,我将不胜感激。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<string>
#include<cstring>
#include<vector>
#include<stack>
#include<sstream>
#include<algorithm>
#include<cctype>
#include<list>
#include<set>
#include<set>
#include<map>
using namespace std;
typedef vector<int> table;
table count(string::iterator beg, string::iterator end)
{

    table F(9);
    std::fill(F.begin(), F.end(), 0);
    if (beg == end)
        return F;

    if (beg + 1 == end) {
        F[(*beg - '0') % 9] = 1;
        return F;
    }

    size_t distance = std::distance(beg, end);
    string::iterator mid = beg + (distance / 2);
    table L = count(beg, mid);
    table R = count(mid, end);

    for (unsigned int i = 0; i < 9; i++) {
        F[i] = L[i] + R[i];
        for(unsigned int j = 0; j < 9; j++) {
            if (j <= i)
                F[i] += L[j] * R[i - j];
            else
                F[i] += L[j] * R[9 + i - j];
        }
    }
    return F;
}

table count(std::string s)
{

    return count(s.begin(), s.end());
}

int main()
{


     cout << count("1234")[0] << endl;

    cout << count("12349")[0] << endl;

    cout << count("9999")[0] << endl;

    cout << count("1292")[0] << endl;cout << count("189")[0] << endl;
    cout << count("10292")[0] << endl;cout << count("0189")[0] << endl;
    system("pause");


   }