我有一个只包含数字0-9的字符串。字符串的长度可以在1到1,000,000个字符之间。我需要在线性时间内找到字符串中不存在的最小数字。以下是一些例子:
1023456789 //Smallest number not in string is 11
1023479 //Smallest number not in string is 5
112131405678910 //Smallest number not in string is 15
大小为1,000,000时,我认为字符串中不存在的最小数字必须至多为6位。
我的方法是生成所有数字0到999,999并将它们全部插入到向量中(按顺序)。然后制作一个标记已经看过的字符串的地图。然后我遍历字符串,对于每个位置,我得到从它开始的所有子字符串,大小为1到6,并且我在地图中将所有这些子字符串标记为true。最后,我只是逐个检查所有键,当我在地图中找到第一个具有错误值的键时,我将其打印出来。
以下是一些代码段:
string tmp="0";
string numbers[999999];
void increase(int pos)
{
if(pos==-1)tmp.insert(0,"1");
else if(tmp.at(pos)!='9')tmp.at(pos)++;
else
{
tmp.at(pos)='0';
increase(pos-1);
}
}
//And later inside main
for(int j=0;j<999999;j++)
{
numbers[j]=tmp;
increase(tmp.size()-1);
}
for(int j=0;j<input.size();j++)
{
for(int k=0;k<6;k++)
{
string temp="";
if(j+k<input.size())
{
temp+=input.at(j+k);
appeared[temp]=true;
}
}
}
int counter=0;
while(appeared[numbers[counter]])counter++;
cout<<numbers[counter]<<endl;
关于算法第一部分的说明。我生成一次矢量,然后我用它100个字符串。我需要在不到4秒的时间内解析所有100个字符串。
这个算法对我来说太慢了,因为它是目前的。我可以优化一些代码,还是应该考虑不同的方法?
答案 0 :(得分:1)
想法是建立一个满足的数字树:
class Node {
public:
Node() : count( 0 ) {}
// create a tree from substring [from, to[ interval
void build( const std::string &str, size_t from, size_t to )
{
Node *node = this;
while( from != to )
node = node->insert( str[from++] );
}
std::string smallestNumber( bool root = true, int limit = 0 ) const;
private:
Node *insert( char c )
{
int idx = c - '0';
if( !children[idx] ) {
++count;
children[idx].reset( new Node );
}
return children[idx].get();
}
int count;
std::unique_ptr<Node> children[10];
};
std::string Node::smallestNumber( bool root, int limit ) const
{
std::string rez;
if( count < 10 ) { // for this node string is one symbol length
for( int i = 0; i < 10; ++i )
if( !children[i] ) return std::string( 1, '0' + i );
throw std::sruntime_error( "should not happen!" );
}
if( limit ) {
if( --limit == 1 ) return rez; // we cannot make string length 1
}
char digit = '0';
for( int i = 0; i < 10; ++i ) {
if( root && i == 0 ) continue;
std::string tmp = children[i]->smallestNumber( false, limit );
if( !tmp.empty() ) {
rez = tmp;
digit = '0' + i;
limit = rez.length();
if( limit == 1 ) break;
}
}
return digit + rez;
}
void calculate( const std::string &str )
{
Node root;
for( size_t i = 0; i < str.length(); ++i ) {
root.build( str, i, i + std::min( 6UL, str.length() - i ) );
}
std::cout << "smallest number is:" << root.smallestNumber() << std::endl;
}
int main()
{
calculate( "1023456789" );
calculate( "1023479" );
calculate( "112131405678910" );
return 0;
}
编辑:经过一番思考后,我意识到内循环是完全没必要的。 1循环就足够了。字符串长度限制为6,我依靠OP估计可能的最大数量。
输出:
smallest number is:11
smallest number is:5
smallest number is:15
答案 1 :(得分:1)
这是我如何解决问题的方法。我们的想法是生成一组特定长度的独特子串,从最短的开始,然后在生成更长的子串之前测试这些子串。这允许代码不对结果的上限进行假设,并且对于具有小结果的长输入字符串也应该快得多。不过,在最糟糕的大结果情况下,它并不一定更好。
int find_shortest_subnumber(std::string str) {
static int starts[10] = {
0, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000
};
// can't find substrings longer than 9 (won't fit in int)
int limit = std::min((int)str.size(), 9);
for(int length = 1; length <= limit; length++) {
std::set<std::string> uniques; // unique substrings of current length
for(int i = 0; i <= (int)str.size() - length; i++) {
auto start = str.begin() + i;
uniques.emplace(start, start + length);
}
for(int i = starts[length - 1]; i < starts[length]; i++) {
if(uniques.find(std::to_string(i)) == uniques.end())
return i;
}
}
return -1; // not found (empty string or too big result)
}
我还没有做过适当的复杂性分析。我粗略地测试了一个特定测试字符串的函数,该字符串长1 028 880
个字符,结果为190 000
。在我的机器上执行大约需要2秒(包括生成测试字符串,这应该可以忽略不计)。
答案 2 :(得分:1)
您可以在线性时间(和空间)中为字符串构造suffix tree。一旦你有了后缀树,你只需要广度优先,按字典顺序扫描每个节点的子节点,并检查每个节点的所有10个数字。第一个缺失的是最小缺失数字的最后一位数。
由于1,000,000个数字序列只有999,995个六位数子序列,因此必须至少有五个六位数的子序列不存在,因此广度优先搜索必须在不晚于第六个级别的情况下终止;因此,它也是线性时间。
答案 3 :(得分:0)
由于您只需要知道是否已经看到某个数字,因此最简单的方法是使用std::vector<bool>
来存储该指示。在浏览输入数字时,在数组中将数字标记为true。完成后,您将遍历数组,并打印出仍然为假的第一个项目的索引。