洛谷题目 魔族密码【js字典树解法】

125 阅读3分钟

字典树讲解

请观看字典树【Tire树】基础讲解 - 掘金 (juejin.cn)学习。

题目链接

请不看下方代码完成本题目魔族密码 - 洛谷,下方代码为其中一种题解,该题解应用了上方讲述内容。

之后思考若是给出数据不是按照字典序时应该怎么处理。

方法1:结合动规思想的字典树解法

思路

此处直接解决非字典序数据版本的题目,当然也适用于原来的题目。

要求一个词链中最多的单词数,由于词链的特性——在一个词链中前一个单词是后一个单词的前缀,所以我们其实可以直接使用字典数来存储每个单词,并在存储字符的过程中计算出该条路径上已经存储的最大单词数。当单词结束时就进行一次标记,并将最大单词数记录在单词所在节点。同时记录从程序运行时到现在的路径上最大单词数。

而当我们当前存储的字符不为当前字串的最后一个字符,但是为之前某一个字串的最后一个字符时,我们应该查看之前存储在此位置上的最大单词数,与以当前字串中当前字符之前的字符所构成的最长词链的单词数 进行比较,将此位置的最大单词数更新,确保他一定为最大的。

这样在不断的处理过程中,我们就可以保证我们每次在节点中存储的数据都是当前数据构成词链的最大单词数。我们自然就可以得出题目中所求的整体的数据所构成的词链的最大单词数。

最终代码

const readline = require('readline');
const { finished } = require('stream');
const rl = readline.createInterface({
    input:process.stdin,
    output:process.stdout
});
let countLine = 1
let N;
let tireTree = { children: null }, inx = 0, res = 0;
let item;
rl.on('line', function(line) {
    if(countLine === 1) {
        //接收总输入行数
        N = parseInt(line);
    } else {
        // 处理数据
        item = line;
        if(countLine <= N + 1) {
            // 字符串变为数组处理
            item = item.split("");
            let p = tireTree, before = 0;// p用于遍历存储,
            //  before用于存储:当前字符之前的最长词链的单词数
            item.forEach((it, index) => {
                // 存储的过程
                if (!p.children) {
                    p.children = {};
                }
                p = p.children;
                if (!p[it])
                    p[it] = {};
                p = p[it];
                if (index === item.length - 1) {//标记最后一个字符
                    p.finished = before + 1;
                }
                if (p.finished) {
                    // 在字串结束时,存储当前词链的最大单词数。
                    p.finished = before = Math.max(before, p.finished);
                    // 获取最大单词数
                    res = Math.max(before, res);
                }
            })
        }
        if (countLine === N + 1) {
            console.log(res)
        }
    }

        countLine++;
})

方法2:普通字典树解法

思路

本思路我们回归题目,不解决非字典树数据,仅考虑题目原本要求。

我们仍使用字典树存储,但是由于数据是字典序的,所以我们可以直接利用本字符前的字符组成的词链的单词个数来进行最大值的推算,每次单词结束时,将原来最大值加1,并存储在字符所处节点中。

这样不断操作就可得到最大单词数。

最终代码

const readline = require('readline');
const { finished } = require('stream');
const rl = readline.createInterface({
    input:process.stdin,
    output:process.stdout
});
let countLine = 1
let N;
let tireTree = [], inx = 0, res = 0;
let item;
rl.on('line', function(line) {
    if(countLine === 1) {
        //求和
        N = parseInt(line);
    } else {
        // 处理input
        item = line;
        if(countLine <= N + 1) {
            item = item.split("");
            let p = 0, before = 0;
            item.forEach((it, index) => {
                if (tireTree[p] === undefined) {
                    tireTree[p] = {};
                }
                if (tireTree[p][it] === undefined) {
                    tireTree[p][it] = {};
                    tireTree[p][it].index = ++inx;
                }
                if (index === item.length - 1) {
                    // 单词结束,标记并计算词链单词数
                    tireTree[p][it].count = before + 1;
                    tireTree[p][it].finished = true;
                }
                if (tireTree[p][it].finished){
                    // 每次遇到已结束的单词,计算此时的词链单词数,并筛出最大值
                    tireTree[p][it].count = before = Math.max(tireTree[p][it].count, before);
                    res = Math.max(res, tireTree[p][it].count);
                }
                p = tireTree[p][it].index;
            })
        }
        if (countLine === N + 1) {
            console.log(res)
        }
    }

        countLine++;
})