LeetCode 720. Longest Word In Dictionary

原题:

Given a list of strings words representing an English Dictionary, find the longest word in words that can be built one character at a time by other words in words. If there is more than one possible answer, return the longest word with the smallest lexicographical order.

If there is no answer, return the empty string.

Example 1:

Input: 
words = ["w","wo","wor","worl", "world"]

Output: "world"

Explanation: 
The word "world" can be built one character at a time by "w", "wo", "wor", and "worl".

Example 2:

Input: 
words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]

Output: "apple"

Explanation: 
Both "apply" and "apple" can be built from other words in the dictionary. However, "apple" is lexicographically smaller than "apply".

题目大意

给出一个单词序列,找出其中长度最长的单词,并且该单词是由集合中其它的单词一次追加一个字符构造出来的。
如果存在满足条件的多个长度相同的单词,则只输出按字典序排最小的那个。

分析

  • 给出的序列是无序的,在这种的情况下对于单词元素并不是很好操作,因此可以先排序,排成字典序或者其他便于处理的顺序(这里的存储容器可以选择原有的vector,或者set)
  • 第二步则是对这些排好序的单词进行操作,如果没有对“添加一个字符构造可得下一个单词”正确的理解和处理,很容易陷入困境,暴力求解的思想则是从长度最长的单词开始处理判别,对每个子字符串均做一次判别,看这些子字符串是否存在于集合中(即,对于单词“world”,分别检查”w”, “wo”, “wor”,”worl”是否在集合之中)。

他山之石,可以攻玉(别人的答案)

在提交之后可以看到别人提交的答案,看完后再看看自己的代码便更觉得羞涩难挡,因此去掉了贴自己代码这一步,转而分析别人代码。
这里选择三个做题提交时间较短(4分钟左右即提交)的答案进行分析和学习。

作者@uwi提交的答案:

class Solution {
        public String longestWord(String[] words) {
            Arrays.sort(words, new Comparator() {
                public int compare(String a, String b) {
                    if(a.length() != b.length())return a.length() - b.length();
                    return -a.compareTo(b);
                }
            });
            int n = words.length;
            boolean[] dp = new boolean[n];
            String last = "";   
            for(int i = 0;i < words.length;i++){
                String w = words[i];
                if(w.length() == 1){
                    dp[i] = true;
                    last = w;
                    continue;
                }
                for(int j = 0;j < i;j++){
                    if(dp[j] && w.length() == words[j].length() + 1 && w.startsWith(words[j])){
                        dp[i] = true;
                        last = w;
                        break;
                    }
                }
            }
            return last;
        }
    }   

正如前面的分析的思路相似,主要有两点处理:

  1. 排序
    这里作者采用的排序方法,是对不同长度的单词按照字典序来排,而统一长度的单词按照反字典序来排。
    (例如[“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”],排序后为[“a”, “ap”, “app”, “appl”, “apply”, “apple”, “banana”])
    这种排序的方法为了接下来的动态规划步骤中,返回最小字典序做准备。
  2. 处理
    在处理这部分使用的是动态规划的思想,dp数组表示该单词的子部分是否出现过,如果出现过,则对应的dp值置为true。直到遍历过整个序列。
    注:这里在用c++初始化DP数组的时候,默认初始化为true,故c++下使用的时候,应为
    bool * dp = new bool [words.size()] {false};

作者@shancha的答案:

bool cmp(string &s1, string &s2) {
    if (s1.size() == s2.size()) return s1 < s2;
    return s1.size() > s2.size();
}
class Solution {
public:
    string longestWord(vector<string>& words) {
        set<string> st;
        for (auto str : words) st.insert(str);
        sort(words.begin(), words.end(), cmp);
        for (auto str : words) {
            int sz = str.size();
            string cur = "";
            int flag = 1;
            for (int i = 0; i < sz; i++) {
                cur.push_back(str[i]);
                if (!st.count(cur)) {
                    flag = 0; break;
                }
            }
            if (flag) return str;
        }
        return "";
    }
};

相比于第一个解答,这个答案就简单粗暴了

  1. 排序
    将所有的单词都存到set中,由于set容器在插入时,按照字典序插入平衡,因此在插入完成后,即可理解为已经按字典序排好。然后按照单词长度从长到短来进行排序。
    例如[“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”],排序后为[“banana”, “apple, ” apply”, “appl”, “app” , “ap”, “a” ])

  2. 处理
    对排好的单词进行处理,在set中调用count来查找该单词的子字符串是否存在,由于是按照长度排序,因此找到一个满足条件的单词即可退出返回。

作者@SaveVMK的答案:

class Solution {
    public String longestWord(String[] words) {
        HashSet w2 = new HashSet<>();
        for (String w : words) {
            w2.add(w);
        }
        String lw = "";
        for (String w : words) {
            if (w.length() < lw.length() || (w.length()==lw.length()&&w.compareTo(lw)>=0))
                continue;
            boolean poss = true;
            for (int i = 1; i < w.length(); i++) {
                if (!w2.contains(w.substring(0,i)))
                    poss = false;
            }
            if (poss)
                lw = w;
        }
        return lw;
    }
}
  1. 排序
    与其他两人的做法不同,这里插入到hash_set中,仅仅可以起到的加速检索的作用,没有进行特定要求的排序
  2. 处理
    对集合中的每个单词进行遍历,使用变量lw来保存符合条件的结果(其中,poss标志量决定了该元素是否符合条件,即子字符串均在集合中,而(w.length()==lw.length()&&w.compareTo(lw)>=0)这个条件,则满足按照字典序输出的要求)
    相比于以上两个需要提前排序的解法,这个算法大大降低了复杂度。

第一次做leetcode积累的经验

  • LeetCode中常用的头文件已经包含(如algorithm等),因此遇到需要排序之类的函数,直接调用即可
  • 记STL容器的特点,常用的容器有:vector,set,hash_map
  • 对于C++中的字符串处理函数运用不是很熟练,这里使用到的对字符串进行的操作有:

    1.对单一字符串中的所有字符进行遍历
    2.截取字符串等等小技巧

他山之石,可以攻玉,多看大佬的解法才能开阔思路,提升自己

你可能感兴趣的:(LeetCode,Leetcode刷题指南,leetcode,字符串-字典序)