【问题标题】:Partially match strings in case of List.contains(String)在 List.contains(String) 的情况下部分匹配字符串
【发布时间】:2011-10-02 11:44:01
【问题描述】:

我有一个List<String>

List<String> list = new ArrayList<String>();
list.add("ABCD");
list.add("EFGH");
list.add("IJ KL");
list.add("M NOP");
list.add("UVW X");

如果我执行list.contains("EFGH"),它会返回true。 在list.contains("IJ") 的情况下我可以得到一个真实的吗?我的意思是,我可以部分匹配字符串以查找它们是否存在于列表中吗?

我有一个包含 15000 个字符串的列表。如果它们存在于列表中,我必须检查大约 10000 个字符串。有什么其他(更快的)方法可以做到这一点?

谢谢。

【问题讨论】:

  • “如果是list.contains("IJ"),我可以得到true吗?”当你尝试它时发生了什么?
  • 您是否必须知道它匹配的确切字词,还是只知道它匹配您的字词之一(不知道哪个字词)就足够了?
  • list.stream().anyMatch("yourstring"::contains)
  • Mahi:你有没有尝试过你自己的答案?这与 .contains() 完全没有区别。

标签: java regex


【解决方案1】:

这不是给定问题的直接答案。但我想这个答案将帮助某人使用Apache Commons Collections 部分比较给定和列表中的元素。

final Equator equator = new Equator<String>() {
        @Override
        public boolean equate(String o1, String o2) {
            final int i1 = o1.lastIndexOf(":");
            final int i2 = o2.lastIndexOf(":");
            return o1.substring(0, i1).equals(o2.substring(0, i2));
        }

        @Override
        public int hash(String o) {
            final int i1 = o.lastIndexOf(":");
            return o.substring(0, i1).hashCode();
        }
    };
    final List<String> list = Lists.newArrayList("a1:v1", "a2:v2");
    System.out.println(IteratorUtils.matchesAny(list.iterator(), new EqualPredicate("a2:v1", equator)));

【讨论】:

    【解决方案2】:

    您可以使用IterableUtils 中的Apache Commons Collections

    List<String> list = new ArrayList<String>();
    list.add("ABCD");
    list.add("EFGH");
    list.add("IJ KL");
    list.add("M NOP");
    list.add("UVW X");
    
    boolean hasString = IterableUtils.contains(list, "IJ", new Equator<String>() {
        @Override
        public boolean equate(String o1, String o2) {
            return o2.contains(o1);
        }
    
        @Override
        public int hash(String o) {
            return o.hashCode();
        }
    });
    
    System.out.println(hasString); // true
    

    【讨论】:

      【解决方案3】:

      是的,你可以!有点像。

      您要查找的内容通常称为fuzzy searching or approximate string matching,并且有多种解决此问题的方法。

      例如,使用FuzzyWuzzy 库,您可以根据字符串与特定搜索词的相似程度为所有字符串分配一个分数。实际值似乎是与搜索字符串长度匹配的字符数的整数百分比。

      调用FuzzySearch.extractAll 后,由您决定将字符串视为匹配的最低分数。

      还有其他类似的库值得一试,例如google-diff-match-patchApache Commons Text Similarity API,等等。

      如果你需要一些非常重的东西,你最好的选择可能是LuceneRyan Shillington 也提到过)

      【讨论】:

        【解决方案4】:

        如果在目标字符串中找到 none 个测试字符串,这里有一些使用正则表达式来缩短内部循环的代码。

        public static void main(String[] args) throws Exception {
            List<String> haystack = Arrays.asList(new String[] { "ABCD", "EFGH", "IJ KL", "M NOP", "UVW X" });
            List<String> needles = Arrays.asList(new String[] { "IJ", "NOP" });
        
            // To cut down on iterations, create one big regex to check the whole haystack
            StringBuilder sb = new StringBuilder();
            sb.append(".*(");
            for (String needle : needles) {
                sb.append(needle).append('|');
            }
            sb.replace(sb.length() - 1, sb.length(), ").*");
            String regex = sb.toString();
        
            for (String target : haystack) {
                if (!target.matches(regex)) {
                    System.out.println("Skipping " + target);
                    continue;
                }
        
                for (String needle : needles) {
                    if (target.contains(needle)) {
                        System.out.println(target + " contains " + needle);
                    }
                }
            }
        }
        

        输出:

        Skipping ABCD
        Skipping EFGH
        IJ KL contains IJ
        M NOP contains NOP
        Skipping UVW X
        

        如果你真的想变得可爱,你可以使用二分搜索来识别目标列表的哪些片段匹配,但这可能不值得。

        这取决于您找到热门歌曲的可能性有多大。低命中率将产生良好的结果。高命中率不会比简单的嵌套循环版本好多少。如果一些针击中许多目标,而另一些则没有击中,请考虑反转循环。

        一切都是为了尽快中止搜索路径。

        【讨论】:

          【解决方案5】:

          如果Roadrunner-EX的建议还不够,我相信你正在寻找Knuth–Morris–Pratt algorithm

          时间复杂度:

          • 表算法的时间复杂度为 O(n),预处理时间
          • 搜索算法的时间复杂度为 O(k)

          所以,整个算法的复杂度是O(n + k)。

          • n = 列表大小
          • k = 您正在搜索的模式长度

          Normal Brute-Force 的时间复杂度为 O(nm)

          此外,KMP 算法对于使用相同的搜索字符串进行搜索将采用相同的 O(k) 复杂度,另一方面,对于暴力方法,它将始终为 O(km)。

          【讨论】:

          • O(nm) 和 O(km) 中的 m 是多少?另外,请查看下面我的简单 O(k) 解决方案。为什么那行不通?
          【解决方案6】:

          怎么样:

          java.util.List<String> list = new java.util.ArrayList<String>();
          list.add("ABCD");
          list.add("EFGH");
          list.add("IJ KL");
          list.add("M NOP");
          list.add("UVW X");
          java.util.regex.Pattern p = java.util.regex.Pattern.compile("IJ");
          java.util.regex.Matcher m = p.matcher("");
          for(String s : list)
          {
              m.reset(s);
              if(m.find()) System.out.println("Partially Matched");
          }
          

          【讨论】:

            【解决方案7】:

            作为第二个答案,在重新阅读您的问题后,您还可以从接口List 继承,仅将其专门用于Strings,并覆盖 contains() 方法。

            public class PartialStringList extends ArrayList<String>
            {
                public boolean contains(Object o)
                {
                    if(!(o instanceof String))
                    {
                        return false;
                    }
                    String s = (String)o;
                    Iterator<String> iter = iterator();
                    while(iter.hasNext())
                    {
                        String iStr = iter.next();
                        if (iStr.contain(s))
                        {
                            return true;
                        }
                    }
                    return false;
                }
            }
            

            从您之前的 cmets 来看,这可能不是您想要的速度,但这是否与您要求的更相似?

            【讨论】:

              【解决方案8】:

              您可以遍历列表,然后在每个字符串上调用 contains()。

              public boolean listContainsString(List<string> list. String checkStr)
              {
                  Iterator<String> iter = list.iterator();
                  while(iter.hasNext())
                  {
                      String s = iter.next();
                      if (s.contain(checkStr))
                      {
                          return true;
                      }
                  }
                  return false;
              }
              

              我认为这样的事情应该可以工作。

              【讨论】:

              • 这就是我现在正在做的事情。但是,如果我想部分匹配,这会给我一个错误。此外,我将不得不遍历 15000 个条目 10000 次。
              • 我不确定我是否理解这个问题。我很确定这将按照您的要求在部分匹配时返回 true,尽管这里已经很晚了,所以我可能完全错过了一个疲倦的错误。另外,正如气垫船建议的那样,你知道它们是否会被分开(用空格或其他字符)吗?如果是这样,那会让问题变得更容易。
              【解决方案9】:

              也许您想将每个字符串组放入一个 HashSet 中,并且通过片段,我的意思是不要添加“IJ KL”,而是分别添加“IJ”和“KL”。如果您需要列表和此搜索功能,则可能需要维护两个集合。

              【讨论】:

              • 一种后缀数组。
              猜你喜欢
              • 2018-05-04
              • 2012-06-15
              • 1970-01-01
              • 2019-10-18
              • 1970-01-01
              • 2017-07-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多