Skip to main content

验证外星语词典

题目

某种外星语也使用英文小写字母, 但可能顺序 order 不同. 字母表的顺序(order)是一些小写字母的排列.

给定一组用外星语书写的单词 words, 以及其字母表的顺序 order, 只有当给定的单词在这种外星语中按字典序排列时, 返回 true; 否则, 返回 false.

在 words[i] 和 order 中的所有字符都是英文小写字母.

示例
输入: words = ["hello", "leetcode"], order = "hlabcdefgijkmnopqrstuvwxyz"
输出: true
解释: 在该语言的字母表中, 'h' 位于 'l' 之前, 所以单词序列是按字典序排列的.
输入: words = ["word","world","row"], order = "worldabcefghijkmnpqstuvxyz"
输出: false
解释: 在该语言的字母表中, 'd' 位于 'l' 之后, 那么 words[0] > words[1], 因此单词序列不是按字典序排列的.
输入: words = ["apple","app"], order = "abcdefghijklmnopqrstuvwxyz"
输出: false
解释: 当前三个字符 "app" 匹配时, 第二个字符串相对短一些, 然后根据词典编纂规则 "apple" > "app", 因为 'l' > '∅', 其中 '∅' 是空白字符, 定义为比任何其他字符都小.

题解

看一下字典序的概念, 这个问题就比较好解了:

在英文字典中, 排列单词的顺序是先按照第一个字母以升序排列(即 a, b, c...z 的顺序); 如果第一个字母一样, 那么比较第二个, 第三个乃至后面的字母. 如果比到最后两个单词不一样长(比如, sigh 和 sight), 那么把短者排在前.

/**
* @param {string[]} words
* @param {string} order
* @return {boolean}
*/
var isAlienSorted = function (words, order) {
const aCharCodeAt = 'a'.charCodeAt()
const indexes = new Array(26).fill(0)
for (let i = 0; i < 26; i++) {
// 先 format 出外星人的字典序
indexes[order[i].charCodeAt() - aCharCodeAt] = i
}

for (let i = 1; i < words.length; i++) {
let needCheckLength = true

for (let j = 0; j < Math.min(words[i - 1].length, j < words[i].length); j++) {
// 获取当前 word 和它的前一个 word 的 charCode
const prev = indexes[words[i - 1][j].charCodeAt() - aCharCodeAt]
const curr = indexes[words[i][j].charCodeAt() - aCharCodeAt]

// 如果 prev < curr, 说明这两个字符串是按照字典序排列的, 跳过这次循环就好了
if (prev < curr) {
// 也说明不用比到最后了(看长度), 因为这两个字符串已经是按照字典序排列的了
needCheckLength = false
break
}

// 如果 prev > curr, 说明不是按字典序排列的, 返回 false
if (prev > curr) return false
}

// 如果两个字符串前面几个字符都是相同的, 比如 sigh 和 sight 前四个字母相同
// 那么需要比两个字符串的长度了, 如果 prev 的长度比 curr 的长度大, 也不符合字典序规则, 返回 false
if (needCheckLength && words[i - 1].length > words[i].length) return false
}

return true
}

复杂度分析

时间复杂度: O(m*n), 其中 m 为字符串数组的长度, n 为数组中字符串的平均长度, 每个字符串需要前一个字符串进行比较, 因此时间复杂度为 O(m*n).

空间复杂度: O(c). 其中 c 表示字母表的长度, 需要存储字母表 order 每个字符的字典序索引, 题目中给定的字母表的长度为 c = 26.