一种更有效的方法,可以找到彼此相同的英文单词

时间:2012-11-10 18:23:48

标签: java algorithm

我写了一个小程序,试图在两个相等长度的英语单词之间找到一个连接。 Word A将通过一次更改一个字母转换为Word B,每个新创建的单词必须是英文单词。

例如:

Word A = BANG
Word B = DUST

结果:

BANG -> BUNG ->BUNT -> DUNT -> DUST

我的流程:

  1. 将一个英文单词列表(由109582个单词组成)加载到Map<Integer, List<String>> _wordMap = new HashMap();中,键将是单词长度。

  2. 用户输入2个字。

  3. createGraph创建图表。

  4. 计算这两个节点之间的最短路径

  5. 打印出结果。

  6. 一切都很好,但我对第3步的时间不满意。

    见:

    Completely loaded 109582 words!
    CreateMap took: 30 milsecs
    CreateGraph took: 17417 milsecs
    (HOISE : HORSE)
    (HOISE : POISE)
    (POISE : PRISE)
    (ARISE : PRISE)
    (ANISE : ARISE)
    (ANILE : ANISE)
    (ANILE : ANKLE)
    The wholething took: 17866 milsecs
    

    我对在第3步中创建图表所花费的时间不满意,这是我的代码(我在图中使用JgraphT):

    private List<String> _wordList = new ArrayList();  // list of all 109582 English words
    private Map<Integer, List<String>> _wordMap = new HashMap();  // Map grouping all the words by their length()
    private UndirectedGraph<String, DefaultEdge> _wordGraph =
            new SimpleGraph<String, DefaultEdge>(DefaultEdge.class);  // Graph used to calculate the shortest path from one node to the other.
    
    
    private void createGraph(int wordLength){
    
        long before = System.currentTimeMillis();
        List<String> words = _wordMap.get(wordLength);
        for(String word:words){
            _wordGraph.addVertex(word);  // adds a node
            for(String wordToTest : _wordList){
                if (isSimilar(word, wordToTest)) {        
                    _wordGraph.addVertex(wordToTest);  // adds another node
                    _wordGraph.addEdge(word, wordToTest);  // connecting 2 nodes if they are one letter off from eachother
                }
            }            
        }        
    
        System.out.println("CreateGraph took: " + (System.currentTimeMillis() - before)+ " milsecs");
    }
    
    
    private boolean isSimilar(String wordA, String wordB) {
        if(wordA.length() != wordB.length()){
            return false;
        }        
    
        int matchingLetters = 0;
        if (wordA.equalsIgnoreCase(wordB)) {
            return false;
        }
        for (int i = 0; i < wordA.length(); i++) {
    
            if (wordA.charAt(i) == wordB.charAt(i)) {
                matchingLetters++;
            }
        }
        if (matchingLetters == wordA.length() - 1) {
            return true;
        }
        return false;
    }
    

    我的问题:

    如何改进算法以加快流程?

    对于正在阅读此内容的任何redditor,是的,我在昨天看到来自/ r / askreddit的帖子后创建了这个。

3 个答案:

答案 0 :(得分:17)

这是一个开始的想法:

创建Map<String, List<String>>(或Multimap<String, String>如果您使用的是Guava),并且对于每个单词,一次“删掉”一个字母,并将原始单词添加到列表中那个消失了。所以你最终得到:

.ORSE => NORSE, HORSE, GORSE (etc)
H.RSE => HORSE
HO.SE => HORSE, HOUSE (etc)

此时,给出一个单词,你可以很容易地找到它所有相似的单词 - 只需再次执行相同的过程,但不是添加到地图中,只需获取每个“消隐”的所有值版本

答案 1 :(得分:0)

您可能需要通过分析器运行它以查看大部分时间都在哪里,特别是因为您正在使用库类 - 否则您可能需要付出很多努力但看不到明显的改进。

您可以在开始之前小写所有单词,以避免每次比较时equalsIgnoreCase()。实际上,这在您的代码中是不一致的 - 您最初使用equalsIgnoreCase(),然后以区分大小写的方式比较字符:if (wordA.charAt(i) == wordB.charAt(i))。完全取消equalsIgnoreCase()检查可能是值得的,因为这与以下charAt循环基本相同。

您可以更改比较循环,以便在找到多个不同的字母时提前完成,而不是比较所有字母,然后再检查匹配或不同的字母数。

更新:这个答案是关于优化您当前的代码。我意识到,再次阅读您的问题,您可能会询问其他算法!)

答案 2 :(得分:0)

您可以对相同长度的单词列表进行排序,然后使用for (int i = 0; i < n; ++i) for (int j = i + 1; j < n; ++j) { }种类的循环嵌套。

在isSimilar中计算差异并在2上返回false。