求一个字符串中无重复元素的最长子串。
解法很多。总的来说,因为 java 封装了很多类,类内的函数也多,所以比C++更好实现。
解法一:brute force O(n^3)
枚举每一个子串O(n^2),判断其中有没有重复元素(使用java中的hashset判断某元素是否在集合中O(1),判断子串中有无重复元素O(n),C++标准模板库中的 unordered_map 可以代替),复杂度为 O(n^3)。
解法二:sliding windows O(2n)
设置一个起始位置为 i j 的滑块,不断 extend the range [ i, j ]。同样使用 hashset/unordered_map 维护。若 s[j] 不存在于set,则加上 s[j],j++。否则判断并记录当前子串长度是否为最长,开始坐标移动到上一个 s[j] 的下一个位置,同时将这中间的元素移除set。(起始坐标的设置很关键,否则无法移除这些元素)。最终返回最长的范围。
解法三:
对解法二的优化。只更新map(映射对不能用set存储) 中元素的位置,通过判断 s[j] 与 起始位置的关系来判断 s[j] 是否在 子串中出现过,而不移除元素,直接移动 i 的位置。省去了移除所产生的复杂度,所以O(2n)-O(n)=O(n).
解法四:
对解法三的优化。不使用map存储映射对,因为输入的字符集范围有限(256 for extended ascll),所以使用长度为256的数组存储映射关系即可。
注:使用java的时候,解法三可以用queue实现,不标记起始位置,而直接压入元素或者弹出元素,但是判断某元素是否在队列出现过这个过程,由于c++标准模板库中的queue不支持迭代器,所以无法实现。java中queue.contains()可以实现。
解法三代码:
#include <unordered_map> using namespace std; class Solution { public: int lengthOfLongestSubstring(string s) { unordered_map <int,int> a; int i=0,j=0,ans=0,n=s.length(); for(;j<n;j++){ if (a.count(s[j])){ auto iter=a.find(s[j]); int temp=iter->second; i=temp>i?temp:i; a.erase(s[j]); }//if a.insert(pair<int,int>(s[j],j+1)); ans=(j-i+1)>ans?j-i+1:ans; }//for return ans; } };
注意 i=j 的时候,子串长度为1,所以注意始末坐标的位置含义。