查找长度高达10000的字符串的子序列
Find subsequences of a string whose length is as large as 10,000
我有一个字符串,其大小可以大到"10000"。我必须数那些能被9整除的子项。
子序列:子序列是一种排列,其中保持给定字符串的顺序。例如:如果给定字符串是10292,那么它的一些子序列是1、102、10、19、12、12(12是2的两倍)、129、029、09、092等。一些不是给定字符串子序列的数字是:201(2和0不能在1之前)、921、0291等。
我试着通过比特移位生成给定字符串的所有子序列(幂集),并检查每个字符串是否可以被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("%ldn", number);
//ignore 0 and check if number divisible by 9
if(number!=0&&number%9==0)count++;
}
printf("%ldn", count);
由于一个数字可以被九整除,当且仅当其数字之和可以被九除时,您可以使用O(n)
递归算法来解决这个问题。
其思想如下:在每一步,将子序列一分为二,并(递归地)确定有多少序列的数字之和为i % 9
,其中i
的范围从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
的子序列的基本情况是显而易见的:只需将F[d % 9] = 1
和所有其他条目设置为零。
完整的C++11实现:
#include <iostream>
#include <array>
#include <tuple>
#include <string>
typedef std::array<unsigned int, 9> table;
using std::tuple;
using std::string;
table count(string::iterator beg, string::iterator end)
{
table F;
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(void)
{
using std::cout;
using std::endl;
cout << count("1234")[0] << endl;
cout << count("12349")[0] << endl;
cout << count("9999")[0] << endl;
}
我有个主意!
由于您只需要计数子字符串,所以您不在乎它们实际上是什么。因此,你可以只存储它们可能的总和。
那么,如果你有一个函数,可以组合两个子字符串集的计数表,并给出它们组合的计数,会怎么样?
既然我知道这是一个可怕的解释,我就举一个例子。假设你得到了一个号码:
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的每个组合相加(对于第一个组合,这相当于2
、4
和24
;对于第二个组合,9
、3
和93
):
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
。这对应于子串243
、2493
和9
。不过,你不知道,因为你只存储了计数——幸运的是,你不在乎!
一旦实现,这将为您提供O(n)
性能——您只需要弄清楚如何在O(1)
中组合表。但是嘿,作业,对吧?祝你好运
如果使用int,则不应将其左移太多。如果你这样做了,你就设置了符号位。使用unsigned int。或者不要太左移。如果您坚持int,则可以在完成后右移。
对于
printf("%ldn", count);
printf在显示长int类型时可能会出现问题。你试过cout吗?
下面是根据Akappa算法编写的C++代码。然而,该算法对包含一个或多个0的数字失败,即在"10292"answers"0189"的情况下,但对"1292"answers"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");
}