【问题标题】:Search in Textblock for shortest passage of given keyword在文本块中搜索给定关键字的最短段落
【发布时间】:2016-05-20 10:27:07
【问题描述】:

我有一个任务,但我不确定我应该如何解决这个问题。我有一个想法,但我不知道这是否是解决它的最佳方法。

任务如下: 给定的是一段文本和一些要查找的关键字。我们需要找到一个可以找到所有单词并且使用最少单词的段落。只需要考虑 A-Z 和 a-z 中的字母。

这是一个例子:
文本块:
Ein toller Beispieltext ist der Blindtext。 Er hat ein paar Wörter。模具 ein Beispieltext, der ein paar Wörter hat und auch noch ein paar mehr, 嗯 死泽勒 etwas länger zu machen。 Darüber hinaus ist er nur dafür da, um genügend Testtext zusammenzubekommen。 Dem Text selbst macht das nicht so 维尔澳大利亚Früher einmal mehr, als er noch nicht so selbstbewusst war。豪特 kennt er seine Rolle als Blindtext und fügt sich selbstbewusst ein。厄斯特 ja irgendwie wichtig。 Manchmal jedoch, ganz manchmal, weint er in der Nacht, weil er niemals bis zum Ende gelesen wird。 Doch das hat ja jetzt zum Glück 恩恩德。

这里需要找到的话: 恩 明信片 德 帕尔 沃尔特

结果是 其他人的作品

以下段落也可以是所有单词都可以找到的段落,但段落内部的单词更多,因此不是解决方案: Ein toller Beispieltext ist Blindtext。 Er hat ein paar Wörter。

我的想法是剪掉所有不必要的字母,然后将文本块拆分为空格,以获得所有单词的数组。所以我可以得到单词的位置并计算在一个搜索词的第一次出现和所有其他搜索词的第一次出现之间有多少词。这样我就需要遍历整个数组并比较所有可能的段落长度,然后取最短的。

你认为这是最好的方法吗?或者你能告诉我一个更好的办法来解决这个问题吗?

【问题讨论】:

  • 这是什么意思:“只需要考虑A-Z和a-z的字母。”是不是表示Wörter作者?当一个单词只包含非 a-z 字母时会发生什么,比如法语 à?它们为单词吗?
  • 是的,正如你所说的 wörter 会变得更写,因此“à”这个词会掉出来而不算是一个词。
  • 如果您搜索的单词列表中有两次相同的单词会发生什么?应该忽略重复,还是应该匹配的短语也有两次?
  • 应该忽略重复

标签: php arrays regex string search


【解决方案1】:

您描述的算法可能还可以,但在 “......这样我需要遍历整个数组”时没有明确指定。

完成清理并拆分成单词后,为关键词创建映射会更容易,因此您可以快速知道文本中的单词是否匹配(isset())。然后您可以将文本数组缩减为匹配单词数组(使用array_filter()),仍然保留它们在原始单词数组中出现位置的索引。

然后该算法将遍历该简化数组并跟踪这些单词的窗口(范围)。只要不是所有必要的词都在其中,窗口就会在右侧放大,而当左侧词已经出现在窗口的其他地方时,或者在您找到候选解决方案之后,它会在左侧缩小。这样,您的窗口将穿过整个(减少的)文本数组。您将只跟踪代表最短短语的窗口。所以最后你有最佳解决方案,只需要将窗口边界转换回原始文本数组中的短语。

不区分大小写的匹配可以通过以小写形式存储(使用strtolower),并使用原始大小写字符串(数组格式)来生成输出。

这是一个实现上述算法的函数:

function findFragment($text, $words) {
    // Remove non-A-Z letters
    $text = preg_replace("/[^a-z ]/i", "", $text);
    $words = preg_replace("/[^a-z ]/i", "", $words);
    // Create a map keyed by the words to find, with as value 
    // the number of occurrences in current sub-phrase
    $words_map = array_fill_keys(str_word_count(strtolower($words), 2), 0);
    // Put all words of text in an array
    $text_arr = str_word_count($text, 1);
    $text_low_arr = str_word_count(strtolower($text), 1);
    // Filter only matching words from the text, keeping their original indexes.
    $matches = array_filter($text_low_arr, function ($word) use ($words_map) {
        return isset($words_map[$word]);
    });
    // How many distinct words need to be matched to have a candidate phrase
    $matches_left = count($words_map);
    // Keep track of how long the shortest phrase is
    $min_words = count($text_arr) + 1; // start "infinite"
    // Loop over all matching words as the last word of a possible phrase
    foreach($matches as $i => $match) {
        $phrase[$i] = $match; // Add to the phrase
        $words_map[$match]++; // Increase count for this particular word
        if ($words_map[$match] > 1) continue; // Nothing new was added
        // Additional word found
        $matches_left--;
        if ($matches_left) continue; // Still need more words
        // Phrase has all words
        // Remove words from left which occur elsewhere in the phrase
        while ($words_map[reset($phrase)] > 1) {
            $words_map[reset($phrase)]--;
            unset($phrase[key($phrase)]);
        }
        // How many words are in this phrase?
        $num_words = $i - key($phrase) +1;
        if ($num_words < $min_words) {
            // It is shorter than we had so far
            $min_words = $num_words;
            $best_start = key($phrase);
        }
        // Remove first word from phrase before finding new candidate phrases
        $words_map[reset($phrase)]--;
        unset($phrase[key($phrase)]);
        $matches_left++;
    }
    // return best result
    return implode(" ", array_slice($text_arr, $best_start, $min_words));
}

你可以这样称呼它:

echo findFragment($text, $words);

对于您在问题中给出的示例数据,它会返回所需的答案:

Beispieltext der ein paar Wrter

看到它在eval.in 上运行。

【讨论】:

  • 哇,感谢您的解决方案 :) 这比我的想法好多了。
  • 您对不区分大小写的比较有什么想法吗?但结果应该输出区分大小写
  • 通过在循环前的初始化期间在两个地方应用strtolower 来调整代码和演示以进行不区分大小写的比较。
【解决方案2】:

前进

我认为这是一个两部分的问题:

  1. 我会首先找到所有带有单词 required words 的句子
  2. 用 PHP word count function 计算结果单词

说明

(?<=.\s|.\s\s|^)(?=[^.]*ein)(?=[^.]*Beispieltext)(?=[^.]*der)(?=[^.]*paar)(?=[^.]*Wörter)[^.]*.

此表达式将执行以下操作:

  • 使用多个前瞻结构(?=[^.]DesiredWord) 确保每个所需单词都存在
  • 找到包含所有所需单词的所有句子

示例

现场演示

https://regex101.com/r/lR7uK3/1

示例文本

Ein toller Beispieltext ist der Blindtext。 Er hat ein paar Wörter。 Dies ist ein Beispieltext, der ein paar Wörter hat und auch noch ein paar mehr, um die Zeile etwas länger zu machen。 Darüber hinaus ist er nur dafür da, um genügend Testtext zusammenzubekommen。 Dem Text selbst macht das nicht so viel aus。 Früher einmal mehr, als er noch nicht so selbstbewusst war。 Heute kennt er seine Rolle als Blindtext und fügt sich selbstbewusst ein。 Erist ja irgendwie wichtig。 Manchmal jedoch, ganz manchmal, weint er in der Nacht, weil er niemals bis zum Ende gelesen wird。 Doch das hat ja jetzt zum Glück ein Ende。

示例匹配

Dies ist ein Beispieltext, der ein paar Wörter hat und auch noch ein paar mehr, um die Zeile etwas länger zu machen。

PHP 字数

$Sentence = "Dies ist ein Beispieltext, der ein paar Wörter hat und auch noch ein paar mehr, um die Zeile etwas länger zu machen.";

echo str_word_count($Sentence);

返回:22

【讨论】:

  • 感谢重播,但返回的结果确实需要是完整的。正确的结果是 Beispieltext der ein paar Wörter 而不是 Dies ist ein Beispieltext, der ein paar Wörter hat und auch noch ein paar mehr, um die Zeile etwas länger zu machen。结果可以包含多个句子。例如,当关键字而不是 Beispieltext 是 Blindtext 并且没有“der”时,正确的结果是 Blindtext。 Er hat ein paar Wörter。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 2015-02-03
  • 2015-09-05
  • 2015-06-08
相关资源
最近更新 更多