前缀树小试:字模式

原题地址:字模式

给定一个模式串pattern和一个字符串str,请问str和pattern是否遵循相同的模式。
这里遵循模式指的是一个完全匹配,即在pattern中的每个不同的字母和str中每个非空的单词之间有一个双向映射的模式对应。

您可以认为模式串pattern只包含小写字母,而str包含由单个空间分隔的小写单词组成。

因为要一一对应的关系,所以要双向映射,所以需要两个map: 一个从pattern的字符到对应的单词,一个从单词到字符。

这里我没有使用系统的map,而是实现了一个 前缀树 来练练手,对于一个前缀树,一个节点可以和一个单词做一一对应的映射,对应的单词就是从跟沿着路径到这个节点的那个单词。所以可以在这个节点里存入跟单词对应的数据,就可以实现从单词到这个数据的映射,所以这里存入字符,实现从单词到pattern的字符的映射。

从字符到单词的映射,有多个方案:

  1. unordered_map<char, string>,用char做key, word做value
  2. 因为都是小写字母,所以可以用 string[26]来标记,string[c-'a']就可以得到字符c的word,比较
  3. 因为前缀树里节点跟单词一一对应,所以可以直接存入节点,结合2的思路就是:TrieNode[26]. 相比2的好处是,比较单词相等不用string.compare了,直接比较两个节点是否相等就可以了。前缀树是节点不会删除,让这一点成为了可能。这样做复杂度直接从O(N*L)到了O(N),L是字符串的平均长度。

using namespace std;

namespace TFDataStruct {
    template<class T>
    class Trie{
    public:
        struct TrieNode{
            char ch;
            //对应的字符串的个数
            uint32_t mark = 0;
            T relateData;  //做额外的数据处理
            TrieNode *parent = nullptr;
            unordered_map<char, TrieNode*> childern;
        };
        struct TrieNodeVisitor{
            typedef void (*VisitFunc)(TrieNode *node, string &word, int idx);
            VisitFunc visitF;
            TrieNodeVisitor(VisitFunc visitF):visitF(visitF){};
        };
    private:
    public:
        TrieNode root;
        
        Trie(){};
        Trie(vector<string> &words){
            for (auto &w : words){
                insert(w, nullptr);
            }
        }
        
        /** 插入一个元素,同时最后的节点 */
        TrieNode *insert(string &word, TrieNodeVisitor visitor = nullptr){
            TrieNode *node = &root;
            int idx = 0;
            while (idx<word.length()) {
                auto &next = node->childern[word[idx]];
                if (next == nullptr) {
                    next = new TrieNode();
                    next->ch = word[idx];
                    next->parent = node;
                }
                node = next;
                if (visitor.visitF) visitor.visitF(node, word, idx);
                
                idx++;
            }
            node->mark++;
            
            return node;
        }
        
        int count(string &word){
            TrieNode *node = &root;
            int idx = 0;
            while (idx<word.length()) {
                auto &next = node->childern[word[idx]];
                if (next == nullptr) {
                    return 0;
                }
                node = next;
                idx++;
            }
            
            return node->mark;
        }
        
        bool exist(string &word){
            return count(word)>0;
        }
    };
}


class Solution {
public:
    /**
     * @param pattern: a string, denote pattern string
     * @param teststr: a string, denote matching string
     * @return: an boolean, denote whether the pattern string and the matching string match or not
     */
bool wordPattern(string &pattern, string &teststr) {
    teststr.push_back(' ');
    
    //1. 前缀树记录的是单词到字符的对应关系,每个节点唯一对应一个单词(即从跟到这个节点的路径拼起来的单词),所以用节点存储单词映射的字符;
    //2. markC2W[c]表示c这个字符映射的单词,又因为节点和单词的一一对应关系,所以把单词对应的节点存入
    TFDataStruct::Trie<char> trieW2C;
    TFDataStruct::Trie<char>::TrieNode* charNodes[256];
    memset(charNodes, 0, sizeof(charNodes));
    
    //3. 因为要一一对应的关系,1和2刚好是两个方向
    
    int start=-1, i = 0;
    int wordIdx = 0;
    for (auto &c : teststr){
        
        if (start<0) {
            if (c!=' ') {
                start = i;
            }
        }else{
            if (c==' ') {
                auto str = teststr.substr(start, i-start);
                auto node = trieW2C.insert(str);
                
                auto charNode = charNodes[pattern[wordIdx]];
                if (charNode == nullptr) {
                    if (node->relateData>0) {
                        return false;
                    }
                }else if (node != charNode) {
                    return false;
                }
                
                node->relateData = pattern[wordIdx];
                charNodes[pattern[wordIdx]] = node;
                
                start = -1;
                wordIdx++;
            }
        }
        
        i++;
    }
    
    return true;
}
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容