真正的密码
题目描述
在一行中输入一个字符串数组,如果其中一个字符串的所有以索引0开头的子串在数组中都有,那么这个字符串就是潜在密码,
在所有潜在密码中最长的是真正的密码,如果有多个长度相同的真正的密码,那么取字典序最大的为唯一的真正的密码,求唯一的真正的密码。
输入描述
输出描述
示例1
输入
h he hel hell hello o ok n ni nin ninj ninja
输出
ninja
说明
按要求,hello
、ok
、ninja
都是潜在密码。
检查长度,hello
、ninja
是真正的密码。
检查字典序,ninja
是唯一真正密码。
示例2
输入
a b c d f
输出
f
说明
按要求,a b c d f
都是潜在密码。
检查长度,a b c d f
是真正的密码。
检查字典序,f
是唯一真正密码。
思路解析
这道题目可以使用字典树(Trie树)来解决。
首先,我们需要构建一个字典树来存储所有的字符串。然后,对于每个字符串,我们可以在字典树中进行查找以其首字母开头的所有子串,如果查找到的所有子串都存在于字典树中,那么这个字符串就是一个潜在密码。
接着,我们可以在所有潜在密码中寻找最长的那个,如果有多个长度相同的真正的密码,就取字典序最大的为唯一的真正的密码。
具体实现时,可以采用深度优先搜索(DFS)来在字典树中查找以某个字符串开头的所有子串,同时记录潜在密码的长度和字典序,找到最长的那个即可。
总的时间复杂度为 O ( N ∗ M 2 ) O(N*M^2) O(N∗M2) ,其中 N N N 为字符串数组的长度, M M M 为字符串的平均长度, M 2 M^2 M2 是因为在深度优先搜索中需要枚举所有子串。
参考解题 Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String[] arr = sc.nextLine().split(" ");
System.out.println(getResult(arr));
}
public static String getResult(String[] arr) {
// 字典序升序
Arrays.sort(arr);
// samePre统计相同首字母的字符串
HashMap<Character, ArrayList<String>> samePre = new HashMap<>();
for (String s : arr) {
Character prefix = s.charAt(0);
samePre.putIfAbsent(prefix, new ArrayList<>());
samePre.get(prefix).add(s);
}
// ans用于缓存潜在密码
ArrayList<String> ans = new ArrayList<>();
for (Character prefix : samePre.keySet()) {
// 获取相同首字母的密码
ArrayList<String> list = samePre.get(prefix);
// 由于密码初始时已经按照字典序升序,因此我们从相同首字母的最后一个密码开始找它的所有前缀子串
for (int i = list.size() - 1; i >= 0; i--) {
// 假设pass是潜在密码
String pass = list.get(i);
// 潜在密码一共有pass.length()个前缀子串,如果list数量少于pass.length(),则必然找不到所有前缀子串
if (pass.length() > list.size()) continue;
// 为了方便查找,我们将list压入set集合中
HashSet<String> set = new HashSet<>(list);
int j = pass.length() - 1;
while (j >= 1) {
// 开始构造pass的前缀子串substring
String substring = pass.substring(0, j);
if (set.contains(substring)) {
// 如果找到了当前的前缀子串,则继续找下一个前缀子串
j--;
} else {
// 如果有一个前缀子串找不到,那么说明pass就不是潜在密码
break;
}
}
// 如果j=0,说明当前pass的所有前缀子串都能找到,
if (j == 0) {
// 因此pass就是潜在密码,可以加入ans
ans.add(pass);
// 且此时pass必然是相同首字母中最长的潜在密码,因此该首字母下的其他潜在密码就不需要再找了
break;
}
}
}
// 所有潜在密码优先按照长度降序,长度相同,再按照字典序降序
ans.sort((a, b) -> a.length() != b.length() ? b.length() - a.length() : b.compareTo(a));
// 取最大的
return ans.get(0);
}
}