剑指 Offer 48. 最长不含重复字符的子字符串
思路
方法一:暴力法
对于字符串s,用f[i]表示以s[i]开头的无重复字符的字符串最大长度,
逐一求出f[0], f[1], ... ,f[s.length()-1] ,最后返回这些值中的最大值即可。
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 if(s.empty()) 5 return 0; 6 //f[i]表示以字符s[i]开头的最长无重复字符串 7 vector<int> f(s.size(), 0); 8 unordered_map<char, bool> vis; 9 10 for(int i = 0; i < s.length(); ++i) { 11 vis[s[i]] = true; 12 f[i] = 1; 13 for(int j = i+1; j < s.length(); ++j) { 14 if(!vis[s[j]]) { 15 vis[s[j]] = true; 16 f[i]++; 17 } else { 18 for(int k = i; k <= j; ++k) { 19 if(vis[s[k]]) 20 vis[s[k]] = false; 21 } 22 break; 23 } 24 } 25 } 26 27 int maxlen = -1; 28 for(int i = 0; i < f.size(); ++i) { 29 if(f[i] > maxlen) 30 maxlen = f[i]; 31 } 32 33 return maxlen; 34 } 35 };
复杂度分析
时间复杂度:O(n2)
空间复杂度:O(n)
空间优化:由于最后只需要求f[]数组中的最大值,所以可以只用一个表示最大值的变量来代替f[]数组,这样可以减少n个大小的空间,但空间复杂度还是O(n),因为unordered_map仍然需要O(n)的空间。
以下方法都参考题解:面试题48. 最长不含重复字符的子字符串(动态规划 / 双指针 + 哈希表,清晰图解)
方法二:动态规划
方法2.1 动态规划 + 哈希表
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 if(s.empty()) 5 return 0; 6 unordered_map<char, int> dic; 7 vector<int> dp(s.size()); 8 dp[0] = 1; 9 dic[s[0]] = 0; 10 11 int i = -1; 12 for(int j = 1; j < s.size(); ++j) { 13 //获取索引位置i 14 if(dic.count(s[j]) == 1) 15 i = dic[s[j]]; 16 else 17 i = -1; 18 19 dic[s[j]] = j; //更新哈希表 20 21 if(dp[j-1] < j-i) { 22 dp[j] = dp[j-1] + 1; 23 } else { 24 dp[j] = j-i; 25 } 26 } 27 28 //返回dp数组中的最大值 29 int res = dp[0]; 30 for(int j = 1; j < dp.size(); ++j) { 31 if(dp[j] > res) 32 res = dp[j]; 33 } 34 35 return res; 36 } 37 };
空间优化后的代码如下:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 unordered_map<char, int> dic; 5 int res = 0; 6 7 int i = -1; 8 int tmp = 0; 9 for(int j = 0; j < s.size(); ++j) { 10 if(dic.count(s[j]) == 1) 11 i = dic[s[j]]; 12 else 13 i = -1; 14 15 dic[s[j]] = j; 16 17 if(tmp < j-i) { 18 tmp = tmp + 1; 19 } else { 20 tmp = j-i; 21 } 22 23 res = max(res, tmp); 24 } 25 26 return res; 27 } 28 };
方法2.2 动态规划 + 线性遍历
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int res = 0; 5 int tmp = 0; 6 7 for(int j = 0; j < s.size(); ++j) { 8 int i = j-1; 9 while(i >= 0 && s[i] != s[j]) { 10 --i; //线性查找 11 } 12 13 if(tmp < j-i) { 14 tmp = tmp + 1; 15 } else { 16 tmp = j-i; 17 } 18 19 res = max(res, tmp); 20 } 21 22 return res; 23 } 24 };
方法三:双指针 + 哈希表 (滑动窗口)
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 unordered_map<char, int> dic; 5 int i = -1, res = 0; 6 for(int j = 0; j < s.length(); j++) { 7 if(dic.count(s[j]) == 1) 8 i = max(i, dic[s[j]]); // 更新左指针 i 9 dic[s[j]] = j; // 更新哈希表记录 10 res = max(res, j - i); // 更新结果 11 } 12 13 return res; 14 } 15 };