暴力解法,枚舉全部子字符串組合html
輸入:長度[0,n]的字符串數組
耗時過長---優化
class Solution { public: int lengthOfLongestSubstring(string s) { int len = s.size(); int ans = 1; if(s.empty()){ return 0; } for(int i = 0; i < len-1; i++){ for(int j = i+1; j < len; j++){ if(allUnique(s, i, j)){ ans = max(ans, j-i+1); } } } return ans; } bool allUnique(string ss, int start, int end){ std::set<char> ss_set; for(int i = start; i <= end; i++){ char ch = ss[i]; if(ss_set.count(ch)){ return false; } ss_set.insert(ss[i]); } return true; } };
第二種方法:改進暴力解法spa
子字符串爲[i,j),左閉右開。一旦檢測到新加入字符已存在於已有子字符串中,則返回當前子字符串長度,清空子字符串,左邊界右移(i+1),從新生成新的子字符串。code
依然耗時過長htm
class Solution { public: int lengthOfLongestSubstring(string s) { int len = s.size(); std::cout << "len is : " << len << std::endl; int ans = 1; std::set<char> ss_set; int begin = 0; if(s.empty()){ return 0; } else{ while(begin < len-1) { //std::cout << "begin is : " << begin << std::endl; int tmp = 0; ss_set.clear(); for(int j = begin; j < len; j++){ if(ss_set.count(s[j])){ break; } ss_set.insert(s[j]); tmp++; } begin = begin + 1; ans = max(ans, tmp); } } return ans; } };
第三種:滑窗法blog
改進第二種方法。子字符串爲[i,j),左閉右開。一旦檢測到新加入字符已存在於已有子字符串中,則返回當前子字符串長度,刪除begin位置的字符,窗口右移(i+1,count-1),新加入字符與窗口中的元素進行對比。索引
耗時94ms,消耗內存21.3MB內存
TIP:字符串
1. 經過使用 HashSet 做爲滑動窗口,咱們能夠用 O(1) 的時間來完成對字符是否在當前的子字符串中的檢查;
2. 滑動窗口是數組/字符串問題中經常使用的抽象概念。 窗口一般是在數組/字符串中由開始和結束索引定義的一系列元素的集合,即 [i, j)(左閉,右開)。而滑動窗口是能夠將兩個邊界向某一方向「滑動」的窗口。例如,咱們將 [i, j) 向右滑動 11 個元素,則它將變爲 [i+1,j+1)(左閉,右開)。
class Solution { public: int lengthOfLongestSubstring(string s) { int len = s.size(); std::set<char> ss_set; int ans = 1, begin = 0, count = 0, index = 0; set<char>::const_iterator iter; if(s.empty()){ return 0; } else{ while(begin < len-1) { for(int j = index; j < len; j++){ if(ss_set.count(s[j])){ index = begin + count; break; } ss_set.insert(s[j]); count++; } // for(iter = ss_set.begin(); iter != ss_set.end(); iter++){ // std::cout << *iter << " " ; // } // std::cout << std::endl; ss_set.erase(s[begin]); begin = begin + 1; ans = max(ans, count); count = count - 1; } } return ans; } };
滑窗法比較清晰的表達,引入變量i,j表示窗口的範圍。
注意 i++ 和 ++i 的區別
class Solution { public: int lengthOfLongestSubstring(string s) { int len = s.size(); std::set<char> ss_set; int ans = 1, i = 0, j = 0;if(s.empty()){ return 0; } else{ while(i < len && j < len){ if(!ss_set.count(s[j])){ ss_set.insert(s[j++]); ans = max(ans, j-i); } else{ ss_set.erase(s[i++]); } } } return ans; } };
優化滑窗法
咱們能夠定義字符到索引的映射,而不是使用集合來判斷一個字符是否存在。 當咱們找到重複的字符時,咱們能夠當即跳過該窗口。
也就是說,若是 s[j] 在 [i,j) 範圍內有與 j′ 重複的字符,咱們不須要逐漸增長 i 。 咱們能夠直接跳過 [i,j′] 範圍內的全部元素,並將 ii 變爲 j' + 1j′+1。
class Solution { public: int lengthOfLongestSubstring(string s) { int len = s.size(); int ans = 0, i = 0, j = 0; map<char, int> str_map; for(j; j < len; j++){ if(str_map.count(s[j])){ i = max(i, str_map.find(s[j])->second); } ans = max(ans, j - i + 1); str_map[s[j]] = j+1; //窗口右邊界右移 } return ans; } };