C++ trie数据结构并打印所有子字符串

C++ trie数据结构并打印所有子字符串,c++,algorithm,data-structures,C++,Algorithm,Data Structures,我需要打印所有唯一的子字符串。因此,我构建了一个trie,但无法确定如何打印所有子字符串。 例如,如果输入是aab和aac,那么我希望它打印“a”、“aa”、“aab”、“aac”、“ab”、“ac”、“b”、“c” 本质上,我需要找到一种从字符串集中获得唯一子字符串的方法。我认为trie是个好方法,因为构建trie需要O(n) 下面是我构建trie的代码 #include <string> #include <iostream> #include <vector&

我需要打印所有唯一的子字符串。因此,我构建了一个
trie
,但无法确定如何打印所有子字符串。 例如,如果输入是
aab
aac
,那么我希望它打印
“a”、“aa”、“aab”、“aac”、“ab”、“ac”、“b”、“c”

本质上,我需要找到一种从字符串集中获得唯一子字符串的方法。我认为
trie
是个好方法,因为构建
trie
需要
O(n)

下面是我构建trie的代码

#include <string>
#include <iostream>
#include <vector>

struct trie_node {
    trie_node *(next[26]);

    trie_node() {
        for ( int i = 0; i < 26; ++i) {
            next[i] = (trie_node*)0;
        }
    }
};

trie_node *root;
char cur_substring[2000];
void build_trie(std::string& input) {
    trie_node *ptrie = root;
    for ( std::string::iterator it = input.begin(); it != input.end(); ++it) {
        int i = *it - 'a';
        if (ptrie->next[i] == (trie_node*)0)
            ptrie->next[i] = new trie_node;
        ptrie = ptrie->next[i];
    }
}

void print_sub_strings(trie_node *p_trie, int pos) {
    for (int i = 0; i < 26; i++) {
        if (p_trie->next[i] != (trie_node*)0) {
            cur_substring[pos] = i + 'a';
            print_sub_strings(p_trie->next[i], pos + 1 );
        }
    }
}

如果您通过考虑每个字符串来正确构建Trie结构,那么遍历Trie将为您提供源字符串的每个可能的子字符串。由于尝试的结构,会自动处理重复项

Trie存储不同的字符串,但它不关心它们的子字符串,这些子字符串不是从第一个字母开始的。存储在Trie中的每个字符串都从根节点开始到非根节点。您可以尝试将子字符串从一个非根节点拾取到另一个非根节点,但不能确保子字符串是唯一的

例如,存储字符串“abab”。您可以从根节点到非根节点获取唯一字符串aababaabab。若您尝试从非根节点开始拾取字符串,您将得到

  • abab
  • abab
  • abab
  • abab
  • abab
  • abab

其中,aabb已经存在。您可以尝试在最后一个字母处存储所有子字符串,以避免出现这种情况。例如,当一个新字符串“abcdab”出现时,您需要在Trie中存储“abcdab”、“bcdab”、“cdab”、“dab”和“b”。无论如何,这会使时间复杂度变为O(n^2),而不是O(n)。

如果要获取字符串的所有子字符串(包括不以第一个字母开头的子字符串),则必须将字符串的后缀存储在trie中

也就是说,存储完整的字符串,然后存储不带第一个字母的字符串,然后不带第二个字母的字符串等。这样,trie可以正确处理重复子字符串的删除,当您遍历它时,您将获得所有正确的子字符串。但是请注意,这不是O(n),正如其他人正确指出的那样,这是这类问题不可能的界

然而,这种数据结构的常用用例是快速检索子字符串。如果将后缀的起始位置(可能有多个后缀)存储在每个leaf,则可以很容易地在任意长的子字符串中找到所有出现的子字符串。这是尝试在检索任务(如全文搜索)中发挥优势的地方

编辑


更新后,您将附加到循环中的本地前缀变量,因此当您调查循环中的下一个子变量时,它将具有错误的值。在您的示例中不应该出现的附加值就是由此引起的。您必须在每次迭代中创建一个新的前缀变量,并传递该变量。您可以找到一些带有额外调试输出的更正代码

不可能在O(n)时间内打印所有子字符串,因为某些字符串有O(n^2)个子字符串-例如,abcdefg…z,它有26*25个不同的子字符串。@templatetypedef,我同意,但在trie上进行一次迭代将得到所有子字符串,如果我画trie Pictural,我可以看到如果我正确遍历树,我可以得到所有的子字符串。关于代码的第一个附加注释:不要使用以u或u开头的标识符。将保留以u和uu开头的所有标识符。另外,代码看起来像是在定期做C,现在开始做C++。试着学习C++习语,它们在很多案例中都很有用。这正是我正在考虑处理重复的问题。但是现在我已经构建了trie,我如何迭代它以获得所有的子字符串。@Avinash:据我所见,您已经获得了正确的递归部分,可以遍历到每个节点。现在,只要输入一个递归调用,就打印cur_substring的内容。这应该可以完成任务。a aa aab aac这是我得到的,但我也需要ab和ac。@Avinash:看起来问题在于构建Trie本身。您只考虑以第一个字母开头的子字符串。您必须对其进行更改,以便在每次迭代中,都从不同的字母开始。不幸的是,这意味着您无法得到您希望的O(n)解决方案。关于复杂性:无论如何,打印子字符串的数量比O(n)更接近O(n^2),所以我想这并不重要:)重新考虑
abab
中的子字符串枚举:您忘记了句子中的
ba
“您可以从[…]获取唯一字符串a、ab、aba、abab“。我的意思是,从根节点到非根节点,只能得到a、ab、aba和abab。用Trie的标准用法无法获得ba和bab。啊,谢谢你的解释,对不起,我的大脑正在慢慢苏醒:)顺便说一句,回答得很好。@dda如果这似乎不起作用,我已经编辑了一次代码并进行了更新。@Avinash在你的_iterate()函数中,在进行递归之前,添加字母(前缀+='a'+I;),但是在递归之后,您没有删除这个字母,这导致当它离开a->a->b,然后转到a->a->c时,在最后一个字符串“aab”的末尾添加“c”。字符串变为“aabc”。出于同样的原因,a->a的下一个是a->b,但是您的代码将“b”添加到“aa”的末尾,并再次获得“aab”。修正这个错误,你会得到正确的答案。
#include <string>
#include <iostream>
#include <vector>

const int ALPHABET_SIZE = 26;
char text[2000];
int LEN;

struct trie_node_t { 
    trie_node_t*child_list[ALPHABET_SIZE]; 
    trie_node_t() {
        for(int index = 0; index < ALPHABET_SIZE; index++)
            child_list[index] = (trie_node_t*)0;
    }
};

class Trie {
public:
    Trie():m_root(new trie_node_t) {
    }

    ~Trie() {
        _delete(m_root);
    }

    void _insert(int pos) {
        int lcv, index; 
        trie_node_t* t = m_root;
        for(lcv = pos; lcv < LEN; lcv++) {
            index = text[lcv] - 'a';
            if (t->child_list[index] == (trie_node_t*)0) {
                t->child_list[index] = new trie_node_t;
            }
            t = t->child_list[index];
        }
    }
    void insert() {
        for ( int i = 0; i < LEN; i++) {
            _insert(i);
        }
    }

    void iterate() {
        _iterate(m_root, "");
    }

    void _iterate(trie_node_t *t, std::string prefix) {        
        for (int i = 0; i < ALPHABET_SIZE; i++) {
            if (t->child_list[i] != (trie_node_t*)0) {
                prefix += 'a' + i;
                std::cout << prefix << std::endl;
                _iterate(t->child_list[i], prefix);
            }   
        }
    }   
private: 
    int node_count;
    trie_node_t* m_root;

    void _delete (trie_node_t* t) {
        int index; 
        if (t != (trie_node_t*)0) {
            for(index = 0; index < ALPHABET_SIZE; index++)
                _delete(t->child_list[index]);
            delete t;
        }
    }    
};

int main ( int argc, char** argv) {
    Trie *pTrie =  new Trie();

    strcpy(text,"aab");
    LEN = strlen(text);
    pTrie->insert();

    strcpy(text,"aac");
    LEN = strlen(text);
    pTrie->insert();

    pTrie->iterate();
}
a
aa
aab
aabc
aab
aabc
ab
abc
Press any key to continue . . .