Java 什么';检查一个字符串是否包含另一个字符串的最快方法是什么?

Java 什么';检查一个字符串是否包含另一个字符串的最快方法是什么?,java,arrays,string,Java,Arrays,String,我得到字符串a和b,并检查b是否包含a的确切字符。例如:“ABBA”和“baaaa”返回false,“ABBA”和“ABABAB”返回true。我将每个字符串值做成一个and数组,并检查b是否包含该值,如果包含该值,则删除该值,这样就不会找到它两次 然而,这个方法太慢了,对于一些大字符串来说,显然需要12秒。我一直在尝试,但还没有找到更快的解决方案。如果可以的话,请帮帮我 public static boolean inneholdt(String a, String b) { int

我得到字符串a和b,并检查b是否包含a的确切字符。例如:“ABBA”和“baaaa”返回false,“ABBA”和“ABABAB”返回true。我将每个字符串值做成一个and数组,并检查b是否包含该值,如果包含该值,则删除该值,这样就不会找到它两次

然而,这个方法太慢了,对于一些大字符串来说,显然需要12秒。我一直在尝试,但还没有找到更快的解决方案。如果可以的话,请帮帮我

public static boolean inneholdt(String a, String b)
{
    int k = 0;
    String[] Inn = a.split("(?!^)");

    for (int i = 0; i < Inn.length; i++)
    {
        if(b.contains(Inn[i]))
        {
            b = b.replaceFirst(Inn[i], "");

            k++;
        }
    }

    if(k >= Inn.length)
    {
        return true;
    } else return false;
}
公共静态布尔inneholdt(字符串a、字符串b)
{
int k=0;
字符串[]Inn=a.split((?!^)”;
for(int i=0;i=客栈长度)
{
返回true;
}否则返回false;
}

如果我理解这个问题,有两种主要方法:

  • 对两个字符串的字符数组进行排序,并检查最短数组是否为最长数组的前缀

  • 填充
    映射
    ,该映射统计最短字符串中每个字符的出现次数。然后,遍历最长的字符串并减少遇到的每个字符的计数。如果一个计数器达到0,则将其从映射中删除。如果映射为空,则返回
    true
    。如果在使用了最长字符串的所有字符后,映射没有变为空,则返回
    false

第一种解决方案将花费更长的时间处理长字符串,并且更可能使用大量内存,因为第二种解决方案“压缩”冗余,而排序数组可能包含相同字符的长序列。然而,如果是非常容易写,读和理解的第一个,所以如果你不需要疯狂的表现,它是好的

我将向您展示第二个解决方案的代码:

// in Java 8. For older versions, it is also easy but more verbose
public static boolean inneholdt(String a, String b) {
    if (b.length() > a.length()) return false;

    Map<Character, Integer> countChars = new HashMap<>();
    for (char ch : b.toCharArray()) countChars.put(ch, countChars.getOrDefault(ch, 0) + 1);
    for (char ch : a.toCharArray()) {
        Integer count = countChars.get(ch);
        if (count != null) {
            if (count == 1) countChars.remove(ch);
            else            countChars.put(ch, count - 1);
        }
        if (countChars.isEmpty()) return true;
    }
    return false;
}
这表明
收集器.groupingBy
的实现非常消耗内存。Max的解决方案原则上并不坏,即使它做的工作比它可能做的更多,因为这是一个高级解决方案,所以它无法控制一些实现细节,例如记录分组的方式。查看Java标准库的代码,它看起来像是执行了排序,因此需要一些内存,特别是因为多个线程同时进行排序。我使用默认设置
-Xmx2g
运行了这个程序。我使用
-Xmx4g
重新运行它

My version :
Mean time of 3 ms with lenA = 50000, lenB = 50
Mean time of 1 ms with lenA = 50000, lenB = 500
Mean time of 2 ms with lenA = 50000, lenB = 5000
Mean time of 5 ms with lenA = 50000, lenB = 50000
Mean time of 7 ms with lenA = 5000000, lenB = 5000
Mean time of 17 ms with lenA = 5000000, lenB = 50000
Mean time of 93 ms with lenA = 5000000, lenB = 500000
Mean time of 642 ms with lenA = 5000000, lenB = 5000000
Mean time of 64 ms with lenA = 50000000, lenB = 50000
Mean time of 161 ms with lenA = 50000000, lenB = 500000
Mean time of 836 ms with lenA = 50000000, lenB = 5000000
Mean time of 11962 ms with lenA = 50000000, lenB = 50000000

Max's parallel solution :
Mean time of 45 ms with lenA = 50000, lenB = 50
Mean time of 18 ms with lenA = 50000, lenB = 500
Mean time of 19 ms with lenA = 50000, lenB = 5000
Mean time of 35 ms with lenA = 50000, lenB = 50000
Mean time of 1691 ms with lenA = 5000000, lenB = 5000
Mean time of 1162 ms with lenA = 5000000, lenB = 50000
Mean time of 1817 ms with lenA = 5000000, lenB = 500000
Mean time of 1671 ms with lenA = 5000000, lenB = 5000000
Mean time of 12052 ms with lenA = 50000000, lenB = 50000
Mean time of 10034 ms with lenA = 50000000, lenB = 500000
Mean time of 9467 ms with lenA = 50000000, lenB = 5000000
Mean time of 18122 ms with lenA = 50000000, lenB = 50000000
这一次运行良好,但仍然缓慢。请注意,用于测试不同版本的测试用例,这是一个相当糟糕的基准测试,但我认为它足以显示
收集器。groupingBy
非常消耗内存,不尝试尽快返回是一个很大的缺点


代码是可用的。

如果我理解这个问题,有两种主要方法:

  • 对两个字符串的字符数组进行排序,并检查最短数组是否为最长数组的前缀

  • 填充
    映射
    ,该映射统计最短字符串中每个字符的出现次数。然后,遍历最长的字符串并减少遇到的每个字符的计数。如果一个计数器达到0,则将其从映射中删除。如果映射为空,则返回
    true
    。如果在使用了最长字符串的所有字符后,映射没有变为空,则返回
    false

第一种解决方案将花费更长的时间处理长字符串,并且更可能使用大量内存,因为第二种解决方案“压缩”冗余,而排序数组可能包含相同字符的长序列。然而,如果是非常容易写,读和理解的第一个,所以如果你不需要疯狂的表现,它是好的

我将向您展示第二个解决方案的代码:

// in Java 8. For older versions, it is also easy but more verbose
public static boolean inneholdt(String a, String b) {
    if (b.length() > a.length()) return false;

    Map<Character, Integer> countChars = new HashMap<>();
    for (char ch : b.toCharArray()) countChars.put(ch, countChars.getOrDefault(ch, 0) + 1);
    for (char ch : a.toCharArray()) {
        Integer count = countChars.get(ch);
        if (count != null) {
            if (count == 1) countChars.remove(ch);
            else            countChars.put(ch, count - 1);
        }
        if (countChars.isEmpty()) return true;
    }
    return false;
}
这表明
收集器.groupingBy
的实现非常消耗内存。Max的解决方案原则上并不坏,即使它做的工作比它可能做的更多,因为这是一个高级解决方案,所以它无法控制一些实现细节,例如记录分组的方式。查看Java标准库的代码,它看起来像是执行了排序,因此需要一些内存,特别是因为多个线程同时进行排序。我使用默认设置
-Xmx2g
运行了这个程序。我使用
-Xmx4g
重新运行它

My version :
Mean time of 3 ms with lenA = 50000, lenB = 50
Mean time of 1 ms with lenA = 50000, lenB = 500
Mean time of 2 ms with lenA = 50000, lenB = 5000
Mean time of 5 ms with lenA = 50000, lenB = 50000
Mean time of 7 ms with lenA = 5000000, lenB = 5000
Mean time of 17 ms with lenA = 5000000, lenB = 50000
Mean time of 93 ms with lenA = 5000000, lenB = 500000
Mean time of 642 ms with lenA = 5000000, lenB = 5000000
Mean time of 64 ms with lenA = 50000000, lenB = 50000
Mean time of 161 ms with lenA = 50000000, lenB = 500000
Mean time of 836 ms with lenA = 50000000, lenB = 5000000
Mean time of 11962 ms with lenA = 50000000, lenB = 50000000

Max's parallel solution :
Mean time of 45 ms with lenA = 50000, lenB = 50
Mean time of 18 ms with lenA = 50000, lenB = 500
Mean time of 19 ms with lenA = 50000, lenB = 5000
Mean time of 35 ms with lenA = 50000, lenB = 50000
Mean time of 1691 ms with lenA = 5000000, lenB = 5000
Mean time of 1162 ms with lenA = 5000000, lenB = 50000
Mean time of 1817 ms with lenA = 5000000, lenB = 500000
Mean time of 1671 ms with lenA = 5000000, lenB = 5000000
Mean time of 12052 ms with lenA = 50000000, lenB = 50000
Mean time of 10034 ms with lenA = 50000000, lenB = 500000
Mean time of 9467 ms with lenA = 50000000, lenB = 5000000
Mean time of 18122 ms with lenA = 50000000, lenB = 50000000
这一次运行良好,但仍然缓慢。请注意,用于测试不同版本的测试用例,这是一个相当糟糕的基准测试,但我认为它足以显示
收集器。groupingBy
非常消耗内存,不尝试尽快返回是一个很大的缺点


代码可用。

Java8+lambda表达式

public static boolean inneholdt(String a, String b) {
    // Here we are counting occurrences of characters in the string
    Map<Integer, Long> aCounted = a.chars().parallel().boxed().collect(Collectors.groupingBy(o -> o, Collectors.counting()));
    Map<Integer, Long> bCounted = b.chars().parallel().boxed().collect(Collectors.groupingBy(o -> o, Collectors.counting()));

    // Now we're checking if the second string contains all the characters from the first
    return bCounted.keySet().parallelStream().allMatch(
            x -> bCounted.getOrDefault(x, 0l) >= aCounted.getOrDefault(x, 0l)
    );
}

第一个数字代表@Dici的解决方案,第二个数字代表我的普通流解决方案,第三个数字代表这个答案的版本

Java8+lambda表达式

public static boolean inneholdt(String a, String b) {
    // Here we are counting occurrences of characters in the string
    Map<Integer, Long> aCounted = a.chars().parallel().boxed().collect(Collectors.groupingBy(o -> o, Collectors.counting()));
    Map<Integer, Long> bCounted = b.chars().parallel().boxed().collect(Collectors.groupingBy(o -> o, Collectors.counting()));

    // Now we're checking if the second string contains all the characters from the first
    return bCounted.keySet().parallelStream().allMatch(
            x -> bCounted.getOrDefault(x, 0l) >= aCounted.getOrDefault(x, 0l)
    );
}

第一个数字代表@Dici的解决方案,第二个数字代表我的普通流解决方案,第三个数字代表这个答案的版本

基于第一眼,无需进一步阅读,但:
a.split((?!^)”
如果您使用的是java8,那么只需使用
a.split(“”)
或可能
a.tocharray()
。在任何情况下,避免替换第一个字符串中的每一个字母,否则会造成高昂的成本。-分别计算第一个字符串中的字符怎么样?如果您知道需要查找2个A和2个B,那么(最多)一次遍历另一个字符串就足够了。我将使用
int[]计数器=new int[256]
并开始在这两个字符串中最短的字符串中找到的每个字符的
int
值中添加1,然后遍历第二个字符串,并为每个字符的
int
值减除1。基于第一眼,不使用