image.png

解决思路

双指针-滑动窗口

典型的可以使用滑动窗口的问题
**

  1. 考察从i到j的一个子串s[i,,,j]

image.png

  1. 如果该子串中没有重复的字母,考察j的下一个字母是否与之前的重复

image.png

  1. 如果没有重复,j++。最长的不重复子串变为s[i,….j+1]

image.png

  1. 依次类推,一直到新加入的字母与之前的字母产生了重复

image.png

  1. 此时不能继续向前拓展,[i,…j]为暂时找到的最长子数组。记录其长度
  2. 然后i++,直到把重复的数字刨除出去

image.png

  1. 然后j就可以包含刚才的重复字符,即j++

image.png

  1. 此时就可以获取一个新的没有重复字符的子串
  2. j继续向前扩展

维护一个滑动窗口

如何判断当前字符与之前的子数组是否重复?

设置一个数组 freq[256]
freq[k]存ASCII码为k的字符在子串中出现的频率
如果是0 没有重复,如果是1就有重复
这样就可以用O(1)的时间复杂度来判断每一个字符在当前的子串中是否重复

  1. public int lengthOfLongestSubstring(String s) {
  2. //定义每个字母出现的次数 初始值0 表示没有出现
  3. int freq[] = new int[256];
  4. //滑动窗口为s[l....r]
  5. //l初始为0 r初始为-1 表示[0,-1] 此时滑动窗口内没有数据
  6. int l = 0;
  7. int r = -1;
  8. //用来保存最后的结果
  9. int res = 0;
  10. //如果左边界还可以取值
  11. while(l<s.length()){
  12. //先判断右边是否可以取到 然后判断右边的数字是否已经出现过
  13. //如果没有出现过 右指针移动 将新出现的字母出现次数累加
  14. if( r+1<s.length() && freq[s.charAt(r+1)]==0){
  15. r++;
  16. freq[s.charAt(r)]++;
  17. }
  18. //如果右边的字母已经出现过 去掉左边的数字 将其出现次数-1 并指针右移
  19. else{
  20. freq[s.charAt(l)]--;
  21. l++;
  22. }
  23. //记录最大值
  24. res = res>(r-l+1)?res:r-l+1;
  25. }
  26. return res;
  27. }