Algorithm 将字符串拆分成一系列单词

Algorithm 将字符串拆分成一系列单词,algorithm,search,search-engine,complexity-theory,Algorithm,Search,Search Engine,Complexity Theory,我最近遇到了以下面试问题: 给定一个输入字符串和一个单词字典,实现一个方法,将输入字符串分解为一个空格分隔的字典单词字符串,搜索引擎可能会使用该字符串来表示“你是什么意思?”例如,“applepie”的输入应产生“apple pie”的输出 就复杂性而言,我似乎无法得到最佳解决方案。有没有人对如何有效地做到这一点有什么建议?一个选择是将所有有效的英语单词存储在trie中。完成此操作后,可以开始从根向下遍历trie,跟随字符串中的字母。无论何时找到标记为单词的节点,都有两个选项: 此时中断输入,或

我最近遇到了以下面试问题:

给定一个输入字符串和一个单词字典,实现一个方法,将输入字符串分解为一个空格分隔的字典单词字符串,搜索引擎可能会使用该字符串来表示“你是什么意思?”例如,“applepie”的输入应产生“apple pie”的输出


就复杂性而言,我似乎无法得到最佳解决方案。有没有人对如何有效地做到这一点有什么建议?

一个选择是将所有有效的英语单词存储在trie中。完成此操作后,可以开始从根向下遍历trie,跟随字符串中的字母。无论何时找到标记为单词的节点,都有两个选项:

  • 此时中断输入,或
  • 继续扩展这个词
  • 一旦将输入内容分解成一组合法且没有剩余字符的单词,您就可以声称找到了匹配项。由于每个字母都有一个强制选项(或者您正在生成一个无效的单词,应该停止-或者-您可以继续扩展单词)或两个选项(拆分或继续),因此可以使用穷举递归实现此函数:

    PartitionWords(lettersLeft, wordSoFar, wordBreaks, trieNode):
        // If you walked off the trie, this path fails.
        if trieNode is null, return.
    
        // If this trie node is a word, consider what happens if you split
        // the word here.
        if trieNode.isWord:
            // If there is no input left, you're done and have a partition.
            if lettersLeft is empty, output wordBreaks + wordSoFar and return
    
            // Otherwise, try splitting here.
            PartitinWords(lettersLeft, "", wordBreaks + wordSoFar, trie root)
    
        // Otherwise, consume the next letter and continue:
        PartitionWords(lettersLeft.substring(1), wordSoFar + lettersLeft[0], 
                       wordBreaks, trieNode.child[lettersLeft[0])
    
    在病理学上最糟糕的情况下,这将列出字符串的所有分区,其长度不能是指数级的。但是,只有当您能够以大量以有效英语单词开头的方式对字符串进行分区时,才会出现这种情况,并且在实践中不太可能出现这种情况。但是,如果字符串有许多分区,我们可能会花费大量时间来查找它们。例如,考虑字符串“dododo.”,我们可以用多种方式分割:

    do the redo
    do the red o
    doth ere do
    dot here do
    dot he red o
    dot he redo
    
    为了避免这种情况,您可能希望限制您报告的答案数量,可能是两个或三个

    因为当我们离开trie时,我们切断了递归,如果我们尝试了一个分割,但没有使字符串的剩余部分保持有效,我们会很快检测到这一点


    希望这有帮助

    将此问题描述为一个完美的面试问题,并提供了几种解决方法。从本质上讲,它包括。在这个级别上,它将产生O(2^n)复杂度。一个使用记忆的有效解决方案可以将这个问题归结为O(n^2)。

    看起来这个问题正是我的面试问题,归结到我在《嘈杂频道》中使用的例子。很高兴你喜欢这个解决方案。我很确定,在最差的性能方面,您无法击败我描述的O(n^2)动态编程/记忆解决方案

    如果你的字典和输入不是病态的,你可以在实践中做得更好。例如,如果您可以在线性时间内识别输入字符串的子字符串在字典中(例如,使用trie),并且如果此类子字符串的数量是常量,则整个时间将是线性的。当然,这是很多假设,但实际数据往往比病理最坏情况好得多

    这个问题还有一些有趣的变化,使它变得更难,例如枚举所有有效的分段,根据best的某些定义输出一个best分段,处理一个太大而无法放入内存的词典,以及处理不精确的分段(例如,纠正拼写错误)。请随时在我的博客上发表评论,或者与我联系,以便跟进

    导入java.util.*

    类位置{
    int indexTest,no;
    位置(内部索引扩展,内部编号)
    {
    this.indexTest=indexTest;
    这个。否=否;
    }}类RandomWordCombo{
    静态布尔isCombo(字符串[]dict,字符串测试)
    {
    HashMap dic=新的HashMap();
    堆栈位置=新堆栈();
    for(每个字符串:dict)
    {
    如果(dic.CONTANSKEY(“+每个字符(0)))
    {
    //System.out.println(“它在这里”);
    ArrayList temp=dic.get(“+each.charAt(0));
    温度添加(每个);
    数字输入(“+”每个字符(0),温度);
    }
    其他的
    {
    ArrayList temp=新的ArrayList();
    温度添加(每个);
    数字输入(“+”每个字符(0),温度);
    }
    }
    迭代器it=dic.entrySet().Iterator();
    while(it.hasNext()){
    Map.Entry对=(Map.Entry)it.next();
    System.out.println(“key:+pair.getKey());
    for(String str:(ArrayList)pair.getValue()
    {
    系统输出打印(str);
    }
    }
    位置推动(新位置(0,0));
    而(!pos.isEmpty())
    {
    位置=位置弹出();
    System.out.println(“位置索引:+position.indexTest+“no:+position.no”);
    if(dic.containsKey(“+测试字符(位置索引)))
    {
    ArrayList strings=dic.get(“+test.charAt(position.indexTest));
    
    if(strings.size()>1&&position.no使用python,我们可以编写两个函数,第一个函数
    segment
    返回一段连续文本的第一个切分到给定词典的单词,或者
    None
    如果没有找到这样的切分。另一个函数
    segment\u all
    返回找到的所有切分的列表。最糟糕的情况是复杂度O(n**2),其中n是以字符为单位的输入字符串长度

    这里提出的解决方案可以扩展到包括拼写更正和二元图分析,以确定最可能的分段

    def memo(func):
        '''
        Applies simple memoization to a function
        '''
        cache = {}
        def closure(*args):
            if args in cache:
                v = cache[args]
            else:
                v = func(*args)
                cache[args] = v
            return v
        return closure
    
    
    def segment(text, words):
        '''
        Return the first match that is the segmentation of 'text' into words
        '''
        @memo
        def _segment(text):
            if text in words: return text
            for i in xrange(1, len(text)):
                prefix, suffix = text[:i], text[i:]
                segmented_suffix = _segment(suffix)
                if prefix in words and segmented_suffix:
                    return '%s %s' % (prefix, segmented_suffix)
            return None
        return _segment(text)
    
    
    def segment_all(text, words):
        '''
        Return a full list of matches that are the segmentation of 'text' into words
        '''
        @memo
        def _segment(text):
            matches = []
            if text in words: 
                matches.append(text)
            for i in xrange(1, len(text)):
                prefix, suffix = text[:i], text[i:]
                segmented_suffix_matches = _segment(suffix)
                if prefix in words and len(segmented_suffix_matches):
                    for match in segmented_suffix_matches:
                        matches.append('%s %s' % (prefix, match))
            return matches 
        return _segment(text)
    
    
    if __name__ == "__main__":    
        string = 'cargocultscience'
        words = set('car cargo go cult science'.split())
        print segment(string, words)
        # >>> car go cult science
        print segment_all(string, words)
        # >>> ['car go cult science', 'cargo cult science']
    

    非常感谢,帮助我获得这个美丽的链接!!…wat可以是一个完美的答案..向这个对问题给予如此尊重的人致敬,我在谷歌采访中也被问过同样的问题!!我们有一个外循环运行在字符串长度上(比如i=1:length(s),其中s是输入字符串),一个内循环运行到当前前缀索引i(比如j=1:i).因为我们希望每个后缀只在第一次在字典中查找(其余的查找将在映射中),所以运行时间是O(n^2)。这个推理正确吗
    def memo(func):
        '''
        Applies simple memoization to a function
        '''
        cache = {}
        def closure(*args):
            if args in cache:
                v = cache[args]
            else:
                v = func(*args)
                cache[args] = v
            return v
        return closure
    
    
    def segment(text, words):
        '''
        Return the first match that is the segmentation of 'text' into words
        '''
        @memo
        def _segment(text):
            if text in words: return text
            for i in xrange(1, len(text)):
                prefix, suffix = text[:i], text[i:]
                segmented_suffix = _segment(suffix)
                if prefix in words and segmented_suffix:
                    return '%s %s' % (prefix, segmented_suffix)
            return None
        return _segment(text)
    
    
    def segment_all(text, words):
        '''
        Return a full list of matches that are the segmentation of 'text' into words
        '''
        @memo
        def _segment(text):
            matches = []
            if text in words: 
                matches.append(text)
            for i in xrange(1, len(text)):
                prefix, suffix = text[:i], text[i:]
                segmented_suffix_matches = _segment(suffix)
                if prefix in words and len(segmented_suffix_matches):
                    for match in segmented_suffix_matches:
                        matches.append('%s %s' % (prefix, match))
            return matches 
        return _segment(text)
    
    
    if __name__ == "__main__":    
        string = 'cargocultscience'
        words = set('car cargo go cult science'.split())
        print segment(string, words)
        # >>> car go cult science
        print segment_all(string, words)
        # >>> ['car go cult science', 'cargo cult science']