trie数据结构和打印所有子字符串

时间:2012-01-18 06:06:30

标签: c++ algorithm data-structures

我需要打印所有独特的子字符串。所以我构建了一个trie,但无法弄清楚如何打印所有子字符串。 例如,如果输入为aabaac,那么我希望它能打印"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 );
        }
    }
}

更新1

根据我得到的输入,我重新编写了我的代码,但它似乎也没有用。

#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 . . .

3 个答案:

答案 0 :(得分:1)

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

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

  • a b ab
  • a ba b
  • 一个 bab
  • ab a b
  • ab ab
  • aba b

a ab b 已经存在。您可以尝试将所有子字符串末尾存储在最后一个字母以避免这种情况。例如,当新字符串“ abcdab ”即将来临时,您需要存储“ abcdab ”,“ bcdab ”,“ Trie中的“cdab ”,“ dab ”,“ ab ”和“ b ”。无论如何,这使得时间复杂度变为O(n ^ 2),而不是O(n)。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

即。你做什么,你存储完整的字符串,然后你存储字符串没有第一个字母,然后没有第二个字母等。这样trie处理正确删除重复的子串,当你遍历它时,你将得到所有正确的子串。但请注意,这不是O(n),正如其他人正确指出的那样,这是一个不可能的问题。

这种数据结构的通常用例是快速检索子串。如果你在每次离开时存储你开始后缀的位置(可能有多个),你可以很容易地找到任意长子串中任何子串的所有出现。这是尝试为检索任务发挥作用的地方,例如全文搜索。

修改

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