如何将字符串拆分为单词。例如:“stringintowords” - > “串成字”?

时间:2010-08-12 11:10:07

标签: algorithm nlp dynamic-programming string-split text-segmentation

将字符串拆分为单词的正确方法是什么? (字符串不包含任何空格或标点符号)

例如:“stringintowords” - > “串成字”

请问您应该在这里使用什么算法?

!更新:对于那些认为这个问题仅仅是为了好奇的人。此算法可用于动态域名(“sportandfishing .com” - >“SportAndFishing .com”),此算法目前由aboutus dot org用于动态执行此转换。

13 个答案:

答案 0 :(得分:23)

假设您有一个函数isWord(w),它使用字典检查w是否为单词。为简单起见,我们现在假设您只想知道某个单词w是否可以进行这样的分割。这可以通过动态编程轻松完成。

S[1..length(w)]成为包含布尔条目的表。如果可以拆分S[i]这个词,则w[1..i]为真。然后将S[1] = isWord(w[1])for i=2设置为length(w)计算

S [i] =(isWord [w [1..i]或{2..i}中的任何j:S [j-1]和isWord [j..i])。

如果字典查询是恒定时间,则需要O(长度(w)^ 2)时间。要实际找到分裂,只需将获胜分割存储在设置为true的每个S [i]中。这也可以通过存储所有这些分裂来适应所有解决方案。

答案 1 :(得分:14)

正如这里的许多人所提到的,这是一个标准的,简单的动态编程问题:FalkHüffner给出了最佳解决方案。其他信息:

(a)您应该考虑使用trie实现 isWord ,如果您正确使用(通过逐步测试单词),这将为您节省大量时间。

(b)输入“分段动态规划”可以从大学级别的讲座中获得更详细的答案,如伪代码算法,例如this lecture at Duke's(甚至可以提供一种简单的概率方法)当你有任何字典中没有的单词时,要处理该怎么做。)

答案 2 :(得分:5)

如果你想确保你做对了,你 使用基于字典的方法,这将是极其低效的。您还必须期望从算法中获得多个结果。

例如:windowsteambloghttp://windowsteamblog.com/成名)

  • windows team blog
  • window steam blog

答案 3 :(得分:5)

学术文献中应该有相当多的内容。您要搜索的关键字是word segmentation。例如,This paper看起来很有希望。

一般情况下,您可能希望了解markov modelsviterbi algorithm。后者是一种动态编程算法,可以让您找到字符串的合理分段,而无需对每个可能的分段进行详尽的测试。这里的基本见解是,如果您对前m个字符有n个可能的分段,并且您只想找到最可能的分段,则不需要针对后续字符评估每个分段 - 您只需要继续评估最有可能的一个。

答案 4 :(得分:3)

考虑给定字符串的可能分割数量。如果字符串中包含n个字符,则可能有n-1个要分割的位置。例如,对于字符串cat,您可以在a之前拆分,然后可以在t之前拆分。这导致4种可能的分裂。

您可以将此问题视为选择拆分字符串的位置。您还需要选择将有多少拆分。所以有Sum(i = 0 to n - 1, n - 1 choose i)个可能的分裂。通过Binomial Coefficient Theorem,x和y均为1,这等于pow(2,n-1)。

当然,很多这种计算都依赖于常见的子问题,因此Dynamic Programming可能会加速你的算法。在我的脑海中,计算一个boolean matrix M such M[i,j] is true if and only if the substring of your given string from i to j is a word会有所帮助。您仍然具有指数数量的可能分段,但如果早期分割没有形成单词,您很快就能够消除分段。然后,解是一个整数序列(i0,j0,i1,j1,...),其条件为j sub k = i sub (k + 1)

如果您的目标是正确的骆驼案例网址,我会回避问题并寻找更直接的内容:获取网址的主页,从源HTML中删除任何空格和大小写,然后搜索您的字符串。如果匹配,请在原始HTML中找到该部分并将其返回。你需要一个NumSpaces数组来声明原始字符串中出现了多少空格,如下所示:

Needle:       isashort    
Haystack:     This is a short phrase    
Preprocessed: thisisashortphrase   
NumSpaces   : 000011233333444444 

你的答案来自:

location = prepocessed.Search(Needle)
locationInOriginal = location + NumSpaces[location]
originalLength = Needle.length() + NumSpaces[location + needle.length()] - NumSpaces[location]
Haystack.substring(locationInOriginal, originalLength)

当然,如果madduckets.com在主页的某个地方没有“Mad Duckets”,这会破坏。唉,这是你为避免指数问题而付出的代价。

答案 5 :(得分:1)

这基本上是knapsack problem的变体,所以你需要的是一个完整的单词列表和Wiki中涵盖的任何解决方案。

对于相当大的字典,这将是非常耗费资源和漫长的操作,你甚至无法确定这个问题是否会得到解决。

答案 6 :(得分:1)

创建可能的单词列表,将其从长单词排序为短单词。

检查列表中的每个条目是否与字符串的第一部分相对应。如果它等于,则将其删除并将其附加到带有空格的句子中。重复一遍。

答案 7 :(得分:1)

这可以在没有字典的情况下实际完成(在某种程度上)。从本质上讲,这是一个无监督的分词问题。您需要收集大量域名,应用无监督的分段学习算法(例如Morfessor)并将学习的模型应用于新域名。我不确定它会有多好用(但这会很有趣)。

答案 8 :(得分:1)

实际上,使用字典可以在O(n)时间内解决此问题。更确切地说,在(k + 1) * n最糟糕的情况下,n是字符串中的字符数,k是字典中最长字的长度。

此外,该算法允许您跳过垃圾。

这是我前一段时间创建的Common Lisp中的工作实现:https://gist.github.com/3381522

答案 9 :(得分:0)

最好的办法是将0中的子字符串与字典进行比较,当找到匹配项时,从该点开始提取该字词并开始新的字典搜索...但它会非常容易出错,并且你会遇到复数和撇号(汇,汇)和其他词类的问题。

修改

将“单一情感”变成“单一情感”或“罪恶高兴运动”?

答案 10 :(得分:0)

将字符串拆分为单词的唯一方法是使用字典。虽然这可能是非常耗费资源的。

答案 11 :(得分:0)

我正在研究这个问题并想到也许我可以分享我是如何做到的。 用文字解释我的算法有点太难了,所以也许我可以用伪代码分享我的优化解决方案:

string mainword = "stringintowords";
array substrings = get_all_substrings(mainword);

/** this way, one does not check the dictionary to check for word validity 
 *  on every substring; It would only be queried once and for all, 
 *  eliminating multiple travels to the data storage
 */
string query = "select word from dictionary where word in " + substrings;
array validwords = execute(query).getArray();

validwords = validwords.sort(length, desc);

array segments = [];
while(mainword != ""){
    for(x = 0; x < validwords.length; x++){
        if(mainword.startswith(validwords[x])) {
            segments.push(validwords[x]);
            mainword = mainword.remove(v);
            x = 0;
        }
    }

    /**
     * remove the first character if any of valid words do not match, then start again
     * you may need to add the first character to the result if you want to
     */
    mainword = mainword.substring(1);
}

string result = segments.join(" ");

答案 12 :(得分:0)

一个简单的Java解决方案,其运行时间为O(n ^ 2)。

public class Solution {
    // should contain the list of all words, or you can use any other data structure (e.g. a Trie)
    private HashSet<String> dictionary;

    public String parse(String s) {
        return parse(s, new HashMap<String, String>());
    }

    public String parse(String s, HashMap<String, String> map) {
        if (map.containsKey(s)) {
            return map.get(s);
        }
        if (dictionary.contains(s)) {
            return s;
        }
        for (int left = 1; left < s.length(); left++) {
            String leftSub = s.substring(0, left);
            if (!dictionary.contains(leftSub)) {
                continue;
            }
            String rightSub = s.substring(left);
            String rightParsed = parse(rightSub, map);
            if (rightParsed != null) {
                String parsed = leftSub + " " + rightParsed;
                map.put(s, parsed);
                return parsed;
            }
        }
        map.put(s, null);
        return null;
    }
}