题目
找出来一个字符串中最长不重复子串 例如 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);
}
复制代码
复制代码