Loading

剑指 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 };

 

posted @ 2020-11-08 20:02  拾月凄辰  阅读(120)  评论(0编辑  收藏  举报