#P3787. 无重复字符的最长子串
-
ID: 3131
Tried: 101
Accepted: 18
Difficulty: 6
无重复字符的最长子串
解题思路
使用滑动窗口+ 哈希表(或数组)记录每个字符最近一次出现的位置。用两个指针维护当前“无重复”的窗口 [left, right]
:当右指针遇到已出现过且位置在当前窗口内的字符时,将 left
移到该字符上次出现位置的下一位,从而保证窗口内始终无重复;每次更新答案为窗口长度的最大值。
核心要点:
- 算法:滑动窗口 + 位置表(哈希表/数组)
- 窗口维护规则:如果
last[ch] >= left
,则left = last[ch] + 1
- 每步更新
last[ch] = right
,并用ans = max(ans, right - left + 1)
此方法一次扫描字符串即可完成。
复杂度分析
- 时间复杂度:O(n),每个字符最多被左右指针访问各一次。
- 空间复杂度:O(min(n, Σ)),
Σ
为字符集大小(本题为常见可打印字符集,实际近似 O(Σ))。
代码实现
Python
# 题面功能封装在外部函数,主函数只负责输入输出(ACM 风格)
import sys
def length_of_longest_substring(s: str) -> int:
# last 记录每个字符最后一次出现的下标
last = {}
left = 0 # 当前窗口左端
ans = 0
for right, ch in enumerate(s):
# 若 ch 在窗口内出现过,则移动左端
if ch in last and last[ch] >= left:
left = last[ch] + 1
# 更新 ch 的最近位置
last[ch] = right
# 更新答案
cur_len = right - left + 1
if cur_len > ans:
ans = cur_len
return ans
def main():
# 读取整份输入,取第一行作为字符串(题目为一行字符串,可能为空)
data = sys.stdin.read()
if data == "":
s = ""
else:
lines = data.splitlines()
s = lines[0] if len(lines) > 0 else ""
print(length_of_longest_substring(s))
if __name__ == "__main__":
main()
Java
// 题面功能封装在外部函数,主函数只负责输入输出(ACM 风格)
// 类名统一为 Main;根据数据范围,这里使用 Scanner(默认不用快读)
import java.util.*;
public class Main {
// 返回最长无重复字符连续子串的长度
public static int lengthOfLongestSubstring(String s) {
// 使用哈希表记录字符最后一次出现的位置
Map<Character, Integer> last = new HashMap<>();
int left = 0; // 当前窗口左端
int ans = 0; // 最优答案
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
// 如果 c 在窗口内出现过,则移动左端
if (last.containsKey(c) && last.get(c) >= left) {
left = last.get(c) + 1;
}
// 更新 c 的最近位置
last.put(c, right);
// 更新答案
int curLen = right - left + 1;
if (curLen > ans) ans = curLen;
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.hasNextLine() ? sc.nextLine() : "";
System.out.println(lengthOfLongestSubstring(s));
sc.close();
}
}
C++
// 题面功能封装在外部函数,主函数只负责输入输出(ACM 风格)
// 由于题目字符为英文字母、数字、符号和空格,使用 256 大小数组即可
#include <bits/stdc++.h>
using namespace std;
// 返回最长无重复字符连续子串的长度
int lengthOfLongestSubstring(const string &s) {
// last[c] 记录字符 c 最后一次出现的位置,初始化为 -1
vector<int> last(256, -1);
int left = 0; // 当前窗口左端
int ans = 0; // 最优答案
for (int right = 0; right < (int)s.size(); ++right) {
unsigned char c = static_cast<unsigned char>(s[right]);
// 如果 c 在窗口内出现过,则移动左端
if (last[c] >= left) {
left = last[c] + 1;
}
// 更新 c 的最近位置
last[c] = right;
// 更新答案
ans = max(ans, right - left + 1);
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
if (!getline(cin, s)) s = ""; // 若无输入行,则视为空串
cout << lengthOfLongestSubstring(s) << "\n";
return 0;
}
题目描述
给定一个字符串 s,请你计算其中不含重复字符的最长连续子串的长度。
注意:此处的“子串”必须是连续的字符序列,不是子序列。
输入
一行字符串 s。 0≤∣s∣≤5×104,s 由英文字母、数字、符号和空格组成。
输出
输出一个整数,表示 s 中不含重复字符的最长连续子串的长度。
样例
样例 1
输入
abcabcbb
输出
3
说明
最长无重复子串为 "abc"
,长度为 3。
样例 2
输入
bbbbb
输出
1
说明
最长无重复子串为 "b"
,长度为 1。
样例 3
输入
pwwkew
输出
3
说明
最长无重复子串可以是 "wke"
(或 "kew"
),长度为 3;"pwke"
不是子串。
样例 4
输入
(空串) 输出
0