正则表达式查找所有子字符串和最长的子字符串

Posted

技术标签:

【中文标题】正则表达式查找所有子字符串和最长的子字符串【英文标题】:Regex to find all substrings and longest substring 【发布时间】:2011-10-02 10:13:50 【问题描述】:

我通常会使用字符串库来做这样的事情。但我想知道是否可以使用正则表达式来完成。

我想做以下事情:给定一个搜索字符串

西雅图很棒

我想在给定的句子中找到它的所有子字符串。因此,将正则表达式应用于以下 sentence

西雅图太棒了 西雅图太棒了 太棒了 西雅图

应该给我

西雅图,西雅图太棒了,太棒了,太棒了,西雅图

一个可能有用的限制是,句子总是只有搜索字符串中出现的单词和中间的空格。

注意如果有匹配,它应该是最长的字符串。因此,就像在上面的示例中一样,匹配项不应该是单个单词,而是可能是最长的子字符串。单词之间的顺序也需要保持。这就是为什么

西雅图很棒

在上面的句子中给了我们

太棒了,是和西雅图

我不确定这样的事情是否可以用正则表达式来完成,因为它是贪婪的。对此有任何见解! 我熟悉 C# 和 Java,可以使用它们的任何一个正则表达式库。

【问题讨论】:

【参考方案1】:

我认为你不能用正则表达式来做到这一点。***上有一篇关于longest common subsequence problem 的好文章。

【讨论】:

我知道但为了匹配字符。我需要在这里匹配单词,尽管我可以修改算法。正则表达式让我可以灵活地在单词之间添加更多分隔符(而不仅仅是空格)。【参考方案2】:

没有好办法直接用正则表达式表达这种模式。

您需要列出所有允许的组合:

西雅图很棒|西雅图很棒|西雅图很棒|很棒|很棒

或更简洁地说:

西雅图(是(真棒)?)?|是(真棒)?|真棒

您可以通过编程方式将输入字符串转换为这种格式。

【讨论】:

这对于任何重要的输入字符串来说很快就会变得很麻烦。 正如我所说,您不必手动执行此转换。注意与后缀树的相似性。事实上,如果正则表达式的性能不够好,您可以轻松地直接创建一个基于后缀树的算法,而不是使用正则表达式。 @dtb - 您是在考虑单词的后缀树,还是字符的后缀树?如果是的话,你知道哪里有一个好的泛型后缀树实现?从经验来看,自己写,甚至为角色改编,都不是我所说的“容易”。 (另外,请参阅下面我的替代 impl - 它的速度非常快 - 每个搜索字符串到目标位置的比较只需一个 == 比较,加上更多的匹配项。) @Ed Staub:词的后缀树。实际上,由于树只包含一个句子,因此您实际上不需要构建后缀树,而只需对句子本身进行操作即可。【参考方案3】:

你能进一步描述你的问题吗?这听起来更像是一个搜索引擎,而不是简单的字符串匹配。我强烈建议查看 Apache Lucene——它有一点学习曲线,但它是一个很棒的智能搜索小工具。它处理了很多在处理搜索时遇到的问题。您可以设置命中的评分来完全按照您的描述进行。

【讨论】:

一般情况下,类似“答案”的帖子应保留为 cmets。向 OP 提出问题并提出概括性 建议(不涉及具体问题),您就是在添加评论【参考方案4】:

在 Java 中,未测试。这将返回字符串列表的迭代器。每个列表都是一个匹配的子序列。 只需在要打印的列表成员之间放置空格即可。如果它被大量使用,那么 intern() 的使用可能会很糟糕。

static Iterator<List<String>> getSequences(String squery, String starget)

    List<String> query = Arrays.asList(squery.split(" "));
    for ( int i = 0; i < query.size(); i++)
        query.set(i, query.get(i).intern());
    List<String> target = Arrays.asList(starget.split(" "));;
    for ( int i = 0; i < target.size(); i++)
        target.set(i, target.get(i).intern());

    // Because the strings are all intern'ed, this HashSet acts like we want -
    // If two lists are the same sequence of words, they are equal.
    // It's used to remove duplicates.
    HashSet<List<String>> ret = new HashSet<List<String>>();
    for ( int qBegin = 0; qBegin < query.size(); qBegin++ )     
        for ( int tBegin = 0; tBegin < target.size(); tBegin++ ) 
            for ( int iCursor = 0; 
                  iCursor < min(query.size()-qBegin, target.size()- tBegin); 
                  iCursor++)                
                if ( query.get(qBegin+iCursor)==target.get(tBegin+iCursor) )
                    ret.add(query.subList(qBegin, qBegin+iCursor+1));
                else break;
            
        
    
    return ret.iterator();


static int min(int a, int b)  return (a<b)? a:b; 

【讨论】:

这不正确有几个原因: - 它不返回不是从查询字符串开头开始的子字符串匹配 - 它不会修剪最长的子字符串。我离开它是因为实习黑客可能应该用于任何好的解决方案 - 可能使用 Guava interner 代替。

以上是关于正则表达式查找所有子字符串和最长的子字符串的主要内容,如果未能解决你的问题,请参考以下文章

JAVA正则表达式怎么匹配所有符合要求的子字符串

使用Java使用正则表达式查找更大字符串的子字符串

JavaScript正则表达式修饰符

正则表达式方法

使用正则表达式匹配所有以 4 位数字结尾的子字符串

JS正则表达式