java - 字符串 1 中的最小窗口包含字符串 2 中的所有字符但不包含字符串 3 中的字符

标签 java string algorithm language-agnostic

好的,这是一道面试题。不,它不是 this question 的副本.

给定 3 个字符串 - str1str2str3:

str1 = "spqrstrupvqw"
str2 = "sprt"
str3 = "q"

我们必须在 str1 中找到最小窗口,其中包含所有来自 str2 的任意顺序的字符,但不包含来自 str3 的字符.在这种情况下,答案将是:"strup"

我想出了这段代码:

static String minimumWindow(String str1, String str2, String str3) {

        class Window implements Comparable<Window> {
            int start;
            int end;

            public Window(int start, int end) {
                this.start = start;
                this.end = end;
            }

            public int getEnd() {
                return end;
            }

            public int getStart() {
                return start;
            }

            public int compareTo(Window o) {
                int thisDiff = end - start;
                int thatDiff = o.end - o.start;

                return Integer.compare(thisDiff, thatDiff);
            }

            @Override
            public String toString() {
                return "[" + start + " : " + end + "]";
            }
        }

        // Create Sets of characters for "contains()" check

        Set<Character> str2Chars = new HashSet<>();
        for (char ch: str2.toCharArray()) {
            str2Chars.add(ch);
        }

        Set<Character> str3Chars = new HashSet<>();
        for (char ch: str3.toCharArray()) {
            str3Chars.add(ch);
        }

        // This will store all valid window which doesn't contain characters
        // from str3.
        Set<Window> set = new TreeSet<>();

        int begin = -1;

        // This loops gets each pair of index, such that substring from 
        // [start, end) in each window doesn't contain any characters from str3
        for (int i = 0; i < str1.length(); i++) {
            if (str3Chars.contains(str1.charAt(i))) {
                 set.add(new Window(begin, i));
                 begin = i + 1;
            }
        }

        int minLength = Integer.MAX_VALUE;
        String minString = "";

        // Iterate over the windows to find minimum length string containing all
        // characters from str2
        for (Window window: set) {
            if ((window.getEnd() - 1 - window.getStart()) < str2.length()) {
                continue;
            }

            for (int i = window.getStart(); i < window.getEnd(); i++) {
                if (str2Chars.contains(str1.charAt(i))) {
                      // Got first character in this window that is in str2
                      // Start iterating from end to get last character
                      // [start, end) substring will be the minimum length
                      // string in this window
                     for (int j = window.getEnd() - 1; j > i; j--) {
                        if (str2Chars.contains(str1.charAt(j))) {
                            String s = str1.substring(i, j + 1);

                            Set<Character> sChars = new HashSet<>();
                            for (char ch: s.toCharArray()) {
                                sChars.add(ch);
                            }

                            // If this substring contains all characters from str2, 
                            // then only it is valid window.
                            if (sChars.containsAll(str2Chars)) {
                                int len = sChars.size();
                                if (len < minLength) {
                                    minLength = len;
                                    minString = s;
                                }
                            }
                        }
                    }
                }
            }
        }

    // There are cases when some trailing and leading characters are
    // repeated somewhere in the middle. We don't need to include them in the
    // minLength. 
    // In the given example, the actual string would come as - "rstrup", but we
    // remove the first "r" safely.
        StringBuilder strBuilder = new StringBuilder(minString);

        while (strBuilder.length() > 1 && strBuilder.substring(1).contains("" + strBuilder.charAt(0))) {
            strBuilder.deleteCharAt(0);
        }

        while (strBuilder.length() > 1 && strBuilder.substring(0, strBuilder.length() - 1).contains("" + strBuilder.charAt(strBuilder.length() - 1))) {
            strBuilder.deleteCharAt(strBuilder.length() - 1);
        }

        return strBuilder.toString();
    }

但它并不适用于所有测试用例。它确实适用于此问题中给出的示例。但是当我提交代码时,有 2 个测试用例失败了。不,我不知道它失败的测试用例。

即使在尝试了各种示例输入之后,我也找不到失败的测试用例。有人可以看看代码有什么问题吗?如果有人能提供更好的算法(只是伪代码),我将不胜感激。我知道这确实不是优化的解决方案。

最佳答案

str1 = "spqrstrupvqw"
str2 = "sprt"
str3 = "q"

我们正在寻找 str1 中的最小子串包含所有 str2字符(假定已排序) 并且没有来自str3 的字符..

i = 1 .. str1.length
cursor = 1 .. str2.length

解决方案必须在表格上:

str2.first X X .. X X str2.last

因此,为了检查该子字符串,我们在 str2 上使用光标, 但我们也有避免 str3 的约束字符,所以我们有:

if str3.contain(str1[i])
    cursor = 1
else
    if str1[i] == str2[cursor]
        cursor++

目标检查是:

if cursor > str2.length
    return solution
else
    if i >= str1.length
        return not-found

为了优化,您可以跳到下一个预测:

look-ahead = { str2[cursor] or { X | X in str3 }}

万一str2 未排序:

i = 1 .. str1.length
lookup = { X | X in str2 }

解决方案必须在表格上:

str2[x] X X .. X X str2[x]

因此,为了检查该子字符串,我们使用检查列表 str2 , 但我们也有避免 str3 的约束字符,所以我们有:

if str3.contain(str1[i])
    lookup = { X | X in str2 }
else
    if lookup.contain(str1[i])
        lookup.remove(str1[i])

目标检查是:

if lookup is empty
    return solution
else
    if i >= str1.length
        return not-found

为了优化,您可以跳到下一个预测:

look-ahead = {{ X | X in lookup } or { X | X in str3 }}

代码

class Solution
{
    private static ArrayList<Character> getCharList (String str)
    {
        return Arrays.asList(str.getCharArray());
    }

    private static void findFirst (String a, String b, String c)
    {
        int cursor = 0;
        int start = -1;
        int end = -1;

        ArrayList<Character> stream = getCharList(a);
        ArrayList<Character> lookup = getCharList(b);
        ArrayList<Character> avoid = getCharList(c);

        for(Character ch : stream)
        {
            if (avoid.contains(ch))
            {
                lookup = getCharList(b);
                start = -1;
                end = -1;
            }
            else
            {
                if (lookup.contains(ch))
                {
                    lookup.remove(ch)

                    if (start == -1) start = cursor;

                    end = cursor;
                }
            }

            if (lookup.isEmpty())
                break;

            cursor++;
        }

        if (lookup.isEmpty())
        {
            System.out.println(" found at ("+start+":"+end+") ");
        }
        else
        {
            System.out.println(" not found ");
        }
    }
}

关于java - 字符串 1 中的最小窗口包含字符串 2 中的所有字符但不包含字符串 3 中的字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23228960/

相关文章:

performance - 算法 : how do divide-and-conquer and time complexity O(nlogn) relate?

algorithm - 从彩色背景中检测黑点

java - 使用 hibernate 时在 POJO 中使用构造函数有哪些限制?

algorithm - 优化具有重复矩阵但下标不同的爱因斯坦求和

java - App发布版本中登录配置问题

c - fgets 没有得到最后一个字符

string - 从字符串中删除数字

ios - 从 json 数据中拆分 ios 字符串中的字符串?

java - 每天更新两个数据库,使它们包含相同的有效数据集

java - 通用容器工厂