/**
* 比较是否只有一个字符不同的单词
*/
public static boolean oneCharOff(String word1, String word2) {
if (word1 == null || word2 == null || word1.length() != word2.length()) {
return false;
}
if (word1 == word2) {
return false;
}
for (int i = 0; i < word1.length(); i++) {
if (word1.charAt(i) != word2.charAt(i)) {
if (word1.substring(i + 1, word1.length()) != word2.substring(i + 1, word2.length())) {
return false;
}
}
}
return true;
}
/**
* 比较是否只添加/删除一个字符得到
* */
public static boolean oneCharAdd(String word1, String word2){
if(word1 == null || word2 == null || Math.abs(word1.length() - word2.length()) != 1){
return false;
}
String largeWord = word1.length() > word2.length() ? word2 : word1;
String shortWord = word1.length() > word2.length() ? word1 : word2;
Integer count = 0;
Integer largePis = 0;
for (int i = 0; i < shortWord.length(); i++) {
if(shortWord.charAt(i) != largeWord.charAt(largePis)){
count ++;
if(count > 1){
return false;
}
if(shortWord.charAt(i) != largeWord.charAt(++largePis)){
return false;
}
}
largePis++;
}
return true;
}
/**
* 修改Map中数组对象元素
*/
public static void update(Map<String, List<String>> map, String key, String value) {
List<String> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(value);
}
/**
* 方法一: 时间复杂度 O(N^2)
* 处理basicWords 数组,最终 产出Map>
* 获取以单词为key, 值修改一个字符可组成的新的单词为数组的value值的Map对象
* base数组基数89000, 运行75秒
*/
public static Map<String, List<String>> computeAdjacentWords(List<String> basicWords) {
Map<String, List<String>> adjMap = new HashMap<>();
for (int i = 0; i < basicWords.size(); i++) {
for (int j = i + 1; j < basicWords.size(); j++) {
if (oneCharOff(basicWords.get(i), basicWords.get(j)) || oneCharAdd(basicWords.get(i), basicWords.get(j))) {
update(adjMap, basicWords.get(i), basicWords.get(j));
update(adjMap, basicWords.get(j), basicWords.get(i));
}
}
}
return adjMap;
}
/**
* 方法二
* 处理basicWords 数组,最终 产出Map>
* 获取以单词为key, 值修改一个字符可组成的新的单词为数组的value值的Map对象
* base数组基数89000, 运行16秒
*/
public static Map<String, List<String>> computeAdjacentWords_v1(List<String> basicWords) {
Map<String, List<String>> adjMap = new HashMap<>();
Map<String, List<String>> lengthMap = new HashMap<>();
//先分类,按字符串长度分类
for (String basicWord : basicWords) {
update(lengthMap, String.valueOf(basicWord.length()), basicWord);
}
for (List<String> strings : lengthMap.values()) {
for (int i = 0; i < strings.size(); i++) {
for (int j = i + 1; j < strings.size(); j++) {
if (oneCharOff(strings.get(i), strings.get(j)) || oneCharAdd(strings.get(i), strings.get(j))) {
update(adjMap, strings.get(i), strings.get(j));
update(adjMap, strings.get(j), strings.get(i));
}
}
}
}
return adjMap;
}
以上算法我们先按长度分组后对每个组进行运算。
我们将这个Map代表一个图的邻接表表示方法,在此基础上我们只需要编写一个案例用来运行单源最短路径算法,然后在输出单词序列
我们通过findChain方法利用上面Map邻接表信息和两个要被链接的单词,同时返回一个Map,改Map中,关键字是单词,而相应的值是从first开始的最短词梯上的关键字签名的那个单词
如上面举例中的那个,如果开始单词是zero,关键字five的值是fire, 关键字fire的值是hire, 关键字hire的值是here,等等,只要我们得到这样一个Map链的结构,我们就能从后向前回溯出我们需要的词梯信息,实现方法如下:
/**
* 有权重图最短路径算法 实现词梯
* @param first 词梯开始单词
* @Param second 词梯结束单词
*/
public static List<String> findChain(List<String> baseWords, String first, String second) {
//处理基础数据,得到对应 权重为1 的图基本数据结构 用Map表示的邻接表
Map<String, List<String>> adjacentWords = computeAdjacentWords_v1(baseWords);
LinkedList<String> q = new LinkedList<>();
Map<String, String> previousWords = new HashMap<>();
q.add(first);
while (!q.isEmpty()) {
String current = q.removeFirst();
List<String> currentArray = adjacentWords.get(current);
if (currentArray != null && currentArray.size() > 0) {
for (String adjWord : currentArray) {
//==null 相当于 之前know 的节点属性为以及遍历过,则无需在处理避免 有圈图
if(previousWords.get(adjWord) == null){
//通过map,一层及一层及剥离,最终得到 second--X--Y--Z--first的map链
previousWords.put(adjWord, current);
q.add(adjWord);
}
}
}
}
previousWords.put(first, null);
return getChainFromPreviousMap(previousWords, first, second);
}
//得到词梯队列信息
public static List<String> getChainFromPreviousMap( Map<String, String> previousWords, String first, String second){
LinkedList<String> q = null;
if(previousWords.get(second) != null){
q = new LinkedList<>();
for(String str = second; str != null ; str = previousWords.get(str)){
q.addFirst(str);
}
}
return q;
}
上一篇:数据结构与算法–图论,最短路算法,拓扑排序算法
下一篇:数据结构与算法–图论-深度优先搜索及其应用