将字符串拆分为单词的正确方法是什么? (字符串不包含任何空格或标点符号)
例如:“stringintowords” - > “串成字”
请问您应该在这里使用什么算法?
!更新:对于那些认为这个问题仅仅是为了好奇的人。此算法可用于动态域名(“sportandfishing .com” - >“SportAndFishing .com”),此算法目前由aboutus dot org用于动态执行此转换。
答案 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)
如果你想确保你做对了,你 使用基于字典的方法,这将是极其低效的。您还必须期望从算法中获得多个结果。
例如:windowsteamblog
(http://windowsteamblog.com/成名)
windows
team
blog
window
steam
blog
答案 3 :(得分:5)
学术文献中应该有相当多的内容。您要搜索的关键字是word segmentation。例如,This paper看起来很有希望。
一般情况下,您可能希望了解markov models和viterbi 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;
}
}