馨晴 發表於 2026-3-3 09:00:00

剑指offer-79、最⻓不含重复字符的⼦字符串

<h2 id="题目描述">题目描述</h2>
<p>请从字符串中找出⼀个最⻓的不包含重复字符的⼦字符串,计算该最⻓⼦字符串的⻓度。</p>
<p>数据范围: ⻓度⼩于40000</p>
<p>示例1<br>
输⼊:"abcabcbb"<br>
返回值:3<br>
说明:因为⽆重复字符的最⻓⼦串是"abc",所以其⻓度为 3。</p>
<p>示例2<br>
输⼊:"bbbbb"<br>
返回值:1<br>
说明:因为⽆重复字符的最⻓⼦串是"b",所以其⻓度为 1</p>
<p>示例3<br>
输⼊:"pwwkew"<br>
返回值:3<br>
说明:因为⽆重复字符的最⻓⼦串是 "wke",所以其⻓度为 3。</p>
<h2 id="思路及解答">思路及解答</h2>
<h3 id="暴力枚举">暴力枚举</h3>
<p>双层循环枚举所有子串,检查是否包含重复字符</p>
<pre><code class="language-java">public class Solution {
    public int lengthOfLongestSubstring(String s) {
      if (s == null || s.length() == 0) return 0;
      
      int n = s.length();
      int maxLen = 0;
      
      // 枚举所有可能的子串起始位置
      for (int i = 0; i &lt; n; i++) {
            // 枚举所有可能的子串结束位置
            for (int j = i; j &lt; n; j++) {
                // 检查子串s是否无重复
                if (isUnique(s, i, j)) {
                  maxLen = Math.max(maxLen, j - i + 1);
                } else {
                  break; // 有重复,内层循环可提前结束
                }
            }
      }
      
      return maxLen;
    }
   
    // 检查子串s是否无重复字符
    private boolean isUnique(String s, int left, int right) {
      Set&lt;Character&gt; set = new HashSet&lt;&gt;();
      for (int i = left; i &lt;= right; i++) {
            char c = s.charAt(i);
            if (set.contains(c)) {
                return false;
            }
            set.add(c);
      }
      return true;
    }
}
</code></pre>
<ul>
<li><strong>时间复杂度</strong>:O(n³),外层循环n次,内层循环最多n次,isUnique检查O(n)</li>
<li><strong>空间复杂度</strong>:O(min(n,128)),字符集大小有限</li>
</ul>
<h3 id="滑动窗口基础版">滑动窗口(基础版)</h3>
<p>右指针扩展窗口,左指针收缩窗口以消除重复</p>
<p><strong>窗口定义:</strong></p>
<ul>
<li><code>left</code>:窗口左边界</li>
<li><code>right</code>:窗口右边界</li>
<li><code>window</code>:当前窗口内的字符集合</li>
</ul>
<p><strong>执行过程示例("abcabcbb"):</strong></p>
<pre><code class="language-text">初始: left=0, right=0, window={}, maxLen=0
1. 添加'a': window={a}, right=1, maxLen=1
2. 添加'b': window={a,b}, right=2, maxLen=2
3. 添加'c': window={a,b,c}, right=3, maxLen=3
4. 添加'a'(重复): 移除s='a', left=1
5. 添加'a': window={b,c,a}, right=4, maxLen=3
...
结果: 3
</code></pre>
<pre><code class="language-java">import java.util.HashSet;
import java.util.Set;

public class Solution {
    public int lengthOfLongestSubstring(String s) {
      if (s == null || s.length() == 0) return 0;
      
      Set&lt;Character&gt; window = new HashSet&lt;&gt;();
      int left = 0, right = 0;
      int maxLen = 0;
      int n = s.length();
      
      while (right &lt; n) {
            char c = s.charAt(right);
            
            // 如果当前字符不在窗口中,扩展窗口
            if (!window.contains(c)) {
                window.add(c);
                right++;
                maxLen = Math.max(maxLen, right - left);
            }
            // 如果当前字符已在窗口中,收缩窗口
            else {
                window.remove(s.charAt(left));
                left++;
            }
      }
      
      return maxLen;
    }
}
</code></pre>
<ul>
<li><strong>时间复杂度</strong>:O(2n) = O(n),每个字符最多被访问两次</li>
<li><strong>空间复杂度</strong>:O(min(n,128)),字符集大小固定</li>
</ul>
<h3 id="优化滑动窗口">优化滑动窗口</h3>
<p>遇到重复字符时,左指针直接跳转到重复字符的下一个位置</p>
<pre><code class="language-java">import java.util.HashMap;
import java.util.Map;

public class Solution {
    public int lengthOfLongestSubstring(String s) {
      if (s == null || s.length() == 0) return 0;
      
      Map&lt;Character, Integer&gt; charIndex = new HashMap&lt;&gt;();
      int left = 0, maxLen = 0;
      
      for (int right = 0; right &lt; s.length(); right++) {
            char c = s.charAt(right);
            
            // 如果字符已存在且在当前窗口内
            if (charIndex.containsKey(c) &amp;&amp; charIndex.get(c) &gt;= left) {
                // 左指针直接跳转到重复字符的下一个位置
                left = charIndex.get(c) + 1;
            }
            
            // 更新字符的最后出现位置
            charIndex.put(c, right);
            
            // 更新最大长度
            maxLen = Math.max(maxLen, right - left + 1);
      }
      
      return maxLen;
    }
}
</code></pre>
<ul>
<li>时间复杂度:O(n)</li>
<li>空间复杂度:O(min(n,128))</li>
</ul>
<p>进一步优化:使用数组替代HashMap</p>
<p>ASCII字符只有128个,可使用固定数组,即使用int记录字符最后出现的位置,-1表示未出现</p>
<pre><code class="language-java">public class Solution {
    public int lengthOfLongestSubstring(String s) {
      if (s == null || s.length() == 0) return 0;
      
      int[] lastIndex = new int; // ASCII字符集
      Arrays.fill(lastIndex, -1);    // 初始化为-1表示未出现
      
      int left = 0, maxLen = 0;
      
      for (int right = 0; right &lt; s.length(); right++) {
            char c = s.charAt(right);
            
            // 如果字符在当前窗口内出现过
            if (lastIndex &gt;= left) {
                left = lastIndex + 1; // 直接跳转
            }
            
            // 更新字符的最后出现位置
            lastIndex = right;
            
            // 更新最大长度
            maxLen = Math.max(maxLen, right - left + 1);
      }
      
      return maxLen;
    }
}
</code></pre>
<ul>
<li>时间复杂度:O(n)</li>
<li>空间复杂度:O(1) - 固定128大小</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19648869
頁: [1]
查看完整版本: 剑指offer-79、最⻓不含重复字符的⼦字符串