【问题标题】:String Matching for patterns模式的字符串匹配
【发布时间】:2018-10-04 23:26:55
【问题描述】:

我有 2 串模式 a.{var1}.{var2}b.{var1}.{var2}

如果第一个字符串中的var1 与第二个字符串中的var1 相同,并且第一个字符串中的var2 与第二个字符串中的var2 相同,则两个字符串匹配。

变量可以是任何顺序,例如a.{var1}.{var2}b.{var2}.{var1}

如何高效匹配两个字符串?

示例 1:

String pattern1 = "1.{var1}";
String pattern2 = "2.{var1}";

//Match True = (1.111,2.111)
//Match False = (1.121,2.111)

示例 2:

String pattern1 = "1.{var1}.{var2}";
String pattern2 = "2.{var1}.{var2}";

//Match True = (1.11.22,2.11.22)
//Match False = (1.11.22,2.111.22)

示例 3:

String pattern1 = "1.{var1}.{var2}";
String pattern2 = "2.{var2}.{var1}";

//Match True = (1.22.11,2.11.22)
//Match False = (1.11.22,2.111.22)

那么匹配这两个字符串的最佳方法是什么?

我想匹配这两个字符串以查找它们是否与提到的模式相关。
将此问题扩展到一组字符串,即 Set A 字符串必须与 Set B 中的字符串匹配。最后必须形成满足该匹配算法的字符串对。匹配 Set A 到 Set B 中的所有字符串时,模式将保持不变。

【问题讨论】:

  • 您可能想要对字符串进行标记并比较各个标记。
  • 这看起来很复杂,就像您正在尝试构建自己的某种正则表达式引擎一样。也许更多地解释为什么比赛发生的逻辑。
  • 您是否要比较由小数分隔的数字?如 (5.9278.30) 5 是一组,9278 是另一组,30 是最后一组?
  • 您在哪一部分遇到了问题?
  • 我想对其进行优化,以将一个字符串列表与另一个字符串列表匹配,并且复杂度最低。

标签: java string algorithm data-structures


【解决方案1】:

在结果数组元素上使用String.split(),然后使用String.equals(),分别处理您的三种情况。

拆分后,首先检查两个结果数组的长度是否相同(如果不是,它们不匹配)。如果需要,还可以使用String.equals() 检查第一个元素是否为"1""2"。然后根据长度是 2 还是 3 进行分支。如果长度为 2,请检查匹配是否与示例 1 中的匹配;再次在数组元素上使用String.equals()。如果长度为3,您需要根据您的两个示例2和3检查变量部分的两个顺序。

请记住,String.split() 的参数是一个正则表达式,并且点在正则表达式中具有特殊含义。所以你需要使用.split("\\."),而不是.split(".")

它也应该运行得很快。但是,在您知道需要更好的性能之前不要开始优化。可读性为王。

编辑:我提出自己的解决方案:

public static boolean match(String s1, String s2) {
    String[] a1 = s1.split("\\.", 4);
    String[] a2 = s2.split("\\.", 4);
    if (a1.length != a2.length) {
        return false;
    }
    if (a1[0].equals("1") && a2[0].equals("2")) {
        if (a1.length == 2) {
            return a1[1].equals(a2[1]);
        } else if (a1.length == 3) {
            return (a1[1].equals(a2[1]) && a1[2].equals(a2[2]))
                    || (a1[1].equals(a2[2]) && a1[2].equals(a2[1]));
        }
    }
    return false;
}

使用问题中的 6 个示例进行尝试:

System.out.println("(1.111,2.111)      " + match("1.111", "2.111"));
System.out.println("(1.121,2.111)      " + match("1.121", "2.111"));
System.out.println("(1.11.22,2.11.22)  " + match("1.11.22", "2.11.22"));
System.out.println("(1.11.22,2.111.22) " + match("1.11.22", "2.111.22"));
System.out.println("(1.22.11,2.11.22)  " + match("1.22.11", "2.11.22"));
System.out.println("(1.11.22,2.111.22) " + match("1.11.22", "2.111.22"));

打印出来:

(1.111,2.111)      true
(1.121,2.111)      false
(1.11.22,2.11.22)  true
(1.11.22,2.111.22) false
(1.22.11,2.11.22)  true
(1.11.22,2.111.22) false

【讨论】:

    【解决方案2】:

    这可能不是最有效的方法,但它确实为您提供了预期的输出。

    01/05:在 cmets 中 Ole 指出错误后更新代码::

    private boolean compareStr(String a, String b) {
        ArrayList<String> aList = new 
        ArrayList<String>(Arrays.asList(a.split("\\.")));
        ArrayList<String> bList = new ArrayList<String>(Arrays.asList(b.split("\\.")));
        bList.remove(0);
        aList.remove(0);
    
        if(aList.size() != bList.size())
                return false;
    
        boolean aMatchFlag = false;
        for(int i=0; i< aList.size(); i++){
            if (!bList.contains(aList.get(i))) {
                return false;
            }
        }
        aMatchFlag = true;
        System.out.println("All elements of A are present in B");
        boolean bMatchFlag = false;
        for(int i=0; i< bList.size(); i++){
            if (!aList.contains(bList.get(i))) {
                return false;
            }
        }
        bMatchFlag = true;
        System.out.println("All elements of B are present in A");
    
        if(aMatchFlag && bMatchFlag)
                return true;
        else
                return false;
    }
    

    对于那些也在寻找代码性能的人

    Input:1.11.11, 2.11.11.11
    Compilation time: 1.45 sec, absolute running time: 0.24 sec, cpu time: 0.26 sec, memory peak: 18 Mb, absolute service time: 1,7 sec
    
    Input:1.11.11, 2.11.22
    Compilation time: 1.25 sec, absolute running time: 0.24 sec, cpu time: 0.23 sec, memory peak: 18 Mb, absolute service time: 1,49 sec
    
    Input:1.11.2, 2.11.22
    Compilation time: 1.34 sec, absolute running time: 0.24 sec, cpu time: 0.24 sec, memory peak: 18 Mb, absolute service time: 1,58 sec
    
    
    Input:1.11.2, 2.11.111
    Compilation time: 1.65 sec, absolute running time: 0.28 sec, cpu time: 0.32 sec, memory peak: 18 Mb, absolute service time: 1,94 sec
    

    【讨论】:

    • 如果两者中的第一个元素与模式匹配,则需要检查字符串。我说的是字符串中的“1”和“2”。
    • 我觉得你帮的太多了。提问者将从被引导到正确的方向和必须自己编写实际代码中学到更多。但是,您的代码不正确。它会说1.11.112.11.22 匹配并且1.2.32.111.3 匹配。
    • @Mohammad 对不起,我没有找到你
    • @OleV.V.感谢您指出,我已做出更改以处理您提到的场景
    • @OleV.V.会的,但现在试试吧
    【解决方案3】:

    我想如下:

    string[] arr1 = pattern1.split
    string[] arr2 = pattern2.split
    int hash1 = arr1[0].hashCode() + arr1[1].hashCode();
    int hash2 = arr2[0].hashCode() + arr2[1].hashCode();
    if(hash1 = hash2)=> pattern1 == pattern2
    

    【讨论】:

    • 并不总是有效。它说 11.2212.21 匹配,他们显然不匹配。
    【解决方案4】:

    从字符串中删除模式通过围绕点拆分字符串从字符串中提取变量(假设您的变量内部没有点),放置它们在集合中(集合不保留顺序,因此会自动解决忽略位置的问题),检查集合的相等性

    运行演示:https://ideone.com/5MwOHC

    示例代码:

    final static String pattern1head = "blablabla.";
    final static String pattern2head = "yada yada.";
    
    private static Set<String> extractVars(String v){
        if      (v.startsWith(pattern1head)) { v = v.replace(pattern1head,""); }
        else if (v.startsWith(pattern2head)) { v = v.replace(pattern2head,""); }
        else                                 { return null; }           
    
        return new HashSet<String>(Arrays.asList(v.split("\\.")));
    }
    
    private static void checkEquality(String v1, String v2) {
        System.out.println("\n"+v1+" == "+v2+" ? " + extractVars(v1).equals(extractVars(v2)));  
    } 
    
    
    public static void main (String[] args) throws java.lang.Exception {
        String v1 = "blablabla.123.456";
        String v2 = "yada yada.123.456";
        String v3 = "yada yada.456.123";
        String v4 = "yada yada.123.456789";
    
        checkEquality(v1,v2);
        checkEquality(v1,v3);
        checkEquality(v1,v4);
        checkEquality(v2,v3);
        checkEquality(v2,v4);
    }
    

    输出:

    blablabla.123.456 == yada yada.123.456 ? true
    
    blablabla.123.456 == yada yada.456.123 ? true
    
    blablabla.123.456 == yada yada.123.456789 ? false
    
    yada yada.123.456 == yada yada.456.123 ? true
    
    yada yada.123.456 == yada yada.123.456789 ? false
    

    【讨论】:

      【解决方案5】:

      您可以使用以下 String 类方法:

      boolean regionMatches(int toffset, String other, int ooffset, int len)
      

      测试此字符串的指定区域是否与 String 参数的指定区域匹配。 区域的长度为 len,并从该字符串的索引 tooffset 和另一个字符串的 ooffset 开始。

      忽略大小写:

      boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
      

      更多信息:https://docs.oracle.com/javase/tutorial/java/data/comparestrings.html

      或者尝试从一个字符串动态创建一个正则表达式模式并与其他字符串进行比较......虽然不是一种有效的方法

      【讨论】:

        【解决方案6】:

        这可以按如下方式完成:

        • 在检查第一个字符串和第一个模式是否匹配时,我们提取字符串中与模式中的占位符(var1、var2、...)相对应的值的映射;
        • 在检查第二个字符串和第二个模式是否匹配时,我们还会根据占位符的值检查第二个字符串。

        这很有趣,因为 map 占位符 -> 值对一对(第一个字符串,第一个模式)计算一次, 并可用于检查每一对(第二个字符串,第二个模式)。

        代码中的翻译:从(第一个字符串,第一个模式)创建一个PatternMatcher类型的对象。该对象将包含一个地图valueByPlaceHolder 用于检查其他夫妻。

        以下是代码的相关部分。

        检查字符串和模式是否匹配 + 创建地图:

        private static Optional<Map<String, String>> extractValueByPlaceHolder(
                String[] sChunks, String[] patternChunks) {
            // string and pattern should have the same length
            if (sChunks.length != patternChunks.length)
                return Optional.empty();
        
            Map<String, String> valueByPlaceHolder = new HashMap<>(sChunks.length);
            for (int i = 0; i < patternChunks.length; i++) {
                String patternChunk = patternChunks[i];
                String sChunk = sChunks[i];
                if (isAPlaceHolder(patternChunk)) { // first char = {, last char = }
                    valueByPlaceHolder.put(patternChunk, sChunk); // just get the value
                } else if (!patternChunk.equals(sChunk)) {
                    // if it's not a placeholder, the chunks should be the same in the string
                    // and the pattern
                    return Optional.empty(); 
                }
            }
            return Optional.of(valueByPlaceHolder);
        }
        

        检查其他字符串和其他模式是否匹配+与第一个(字符串,模式)对比较:

        public boolean check(String[] otherChunks, String[] otherPatternChunks) {
            // other string and other pattern should have the same length, other string and string too
            if (otherChunks.length != this.chunks_length || otherChunks.length != otherPatternChunks.length)
                return false;
        
            for (int i = 0; i < otherChunks.length; i++) {
                String otherPatternChunk = otherPatternChunks[i];
                String otherChunk = otherChunks[i];
                // get the value from the first string if a it's placeholder, else keep the pattern chunk 
                String expectedChunk = this.valueByPlaceHolder
                        .getOrDefault(otherPatternChunk, otherPatternChunk);
        
                // the chunk is neither equal to the value of the placeholder, nor to the chunk of the pattern 
                if (!expectedChunk.equals(otherChunk))
                        return false;
            }
            return true;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-08
          • 2016-01-08
          • 1970-01-01
          • 1970-01-01
          • 2013-07-11
          相关资源
          最近更新 更多