找出来一个字符串中最长不重复子串

·  阅读 499

题目

找出来一个字符串中最长不重复子串 例如 12311 最长子串为 123,因为 1231 里面有两个1,在比如 12315 最长子串为 2315

思路

  • 使用可变长的窗口,从起始位置依次查找元素
  • 每遍历一个元素,先判断是否已经在当前窗口中
  • 如果不在,就增大窗口长度,将其包含进了,并记录窗口变化过程中,最大窗口长度和其当时的起始坐标
  • 如果存在,说明遇到了重复元素,这时需要重新调整窗口。窗口的开始位置改为第一个重复元素的下一个位置,结束位置为第二个重复元素的下一个位置

实现

实现一,窗口开始位置+窗口长度

public class Main {

    /**
     * 问题:实现找出来一个字符串中最长不重复子串
     * <p>
     * 120135435 最长非重复子串为:201354
     * abdfkjkgdok最长非重复子串为:abdfkj
     * 123456780423349最长非重复子串为:123456780
     */
    public static void main(String[] args) {
        System.out.println(getMaxSub("120135435"));
    }

    
    public static String getMaxSub(String s) {
        if (s == null || s.length() == 0) {
            return null;
        }

        // 目前能找到的 最大变长窗口的 起始位置
        int maxWindowStartIndex = 0;
        // 目前能找到的 最大变长窗口的 长度
        int maxWindowLength = 0;

        // 当前变长窗口的起始位置
        int nowWindowStartIndex = 0;
        // 当前变长窗口的长度
        int nowWindowLength = 0;

        Map<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            Integer charIndex = map.get(ch);
            // 当前字符在 map 已存,并且字符的位置在当前窗口中,即当前窗口中出现了两个相同字符
            if (charIndex != null && charIndex >= nowWindowStartIndex) {
                // 重新设置窗口
                // 起始位置为重复字符的下一个位置
                nowWindowStartIndex = charIndex + 1;
                //长度为两个重复字符之间的间隔
                nowWindowLength = i - charIndex;
            } else {
                // 窗口增大
                nowWindowLength++;
                if (nowWindowLength > maxWindowLength) {
                    // 若当前窗口的长度>目前找到到最长子串的长度,则更新历史最大窗口的起始位置和长度
                    maxWindowStartIndex = nowWindowStartIndex;
                    maxWindowLength = nowWindowLength;
                }
            }
            // 无论存在不存在重复字符,都要把遍历的 字符存入map中,重复的则覆盖
            map.put(ch, i);
        }
        return s.substring(maxWindowStartIndex, (maxWindowStartIndex + maxWindowLength));
    }
}
复制代码

实现二,窗口开始位置+窗口结束位置(可能更好理解)

public static void main(String[] args) {
    System.out.println(getMaxSub("120135435"));
    System.out.println(getMaxSub("abdfkjkgdok"));
    System.out.println(getMaxSub("123456780423349"));
}

/**
 * 问题:实现找出来一个字符串中最长不重复子串
 * <p>
 * 120135435 最长非重复子串为:201354
 * abdfkjkgdok 最长非重复子串为:abdfkj
 * 123456780423349 最长非重复子串为:123456780
 */
public static String getMaxSub(String s) {
    if (s == null || s.length() == 0) {
        return null;
    }

    Map<Character, Integer> map = new HashMap<>();

    // 目前能找到的 最大变长窗口的 起始位置
    int maxWindowStartIndex = 0;
    // 目前能找到的 最大变长窗口的 结束位置
    int maxWindowEndIndex = 0;

    // 当前变长窗口的起始位置
    int nowWindowStartIndex = 0;
    // 当前变长窗口的结束位置
    int nowWindowEndIndex = 0;

    for (int i = 0; i < s.length(); i++) {
        char ch = s.charAt(i);
        // 查看当前字符之前是否出现过
        Integer charIndex = map.get(ch);
        // 没找到,或者找到了,但不在当前窗口中
        if (charIndex == null || charIndex < nowWindowStartIndex) {
            // 窗口增大
            nowWindowEndIndex++;
            // 当前窗口长度大于历史最大窗口长度
            if ((nowWindowEndIndex - nowWindowStartIndex) > (maxWindowEndIndex - maxWindowStartIndex)) {
                // 则更新历史最大窗口的起始位置和长度
                maxWindowStartIndex = nowWindowStartIndex;
                maxWindowEndIndex = nowWindowEndIndex;
            }
        } else {
            // 当前字符在 map 已存,并且字符的位置在当前窗口中,即当前窗口中出现了两个相同字符,重新设置窗口
            // 起始位置为第一个重复字符的下一个位置
            nowWindowStartIndex = charIndex + 1;
            // 结束位置为第二个重复字符的下一个位置
            nowWindowEndIndex = i + 1;
        }
        // 无论存在不存在重复字符,都要把遍历的 字符存入map中,重复的则覆盖
        map.put(ch, i);
    }
    // 截取时,要包含窗口开始,也包含窗口结束位置
    return s.substring(maxWindowStartIndex, maxWindowEndIndex);
}
复制代码
复制代码
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改