题目:给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
在解答这个题目之前我还是先说一下我在写这道题时候的思路和想法,其实这道题也不怎么难我阅读题目不久以后就已经想到了解决方案,那我是怎么想的呢?对于我这种头脑简单的来说肯定想到的是暴力解决毕竟现在接触的算法不多能想到的也有限,那我是怎么解决的呢?因为要找最长无重复字串所以要做的是要找到字符串中的不重复字符所以先把第一个字符保存起来那保存在哪呢?我们就先定义一个动态数组用来保存第一个字符,第一个字符保存在动态数组中以后让字符串中的第二个字符与已经保存在动态数组中的字符进行依次比较,如果不一样就让第二个字符也保存在动态数组中,再让第三个字符与动态数组中的字符进行比较不一样则再让第三个字符保存在动态数组中,依次让下一个字符与以保存在动态数组中的字符进行比较这样就可以让不重复的字符存在一起,如果第四个字符与动态数组中的字符有重复的那么就先得到当前已经保存在动态数组中字符串长度,再定义一个动态数组把该字符串长度保存在动态数组中,就将保存字符串的动态数组清空现在保存字符串的动态数组已经为空就把第二个字符保存在动态数组中,重复以上的操作就可以实现字符串中的每个字符开头的不重复子串不会存在没有遍历到的字符,最后找到保存字符串长度的动态数组中最大的数字这个数字就是无重复字串的最大长度。以上面给出的s = "abcabcbb"字符串对比文字讲解就可以看懂算法是怎么实现的具体代码细节就不再深究自己可以慢慢看,代码还可以优化请自行优化。
代码:
int lengthOfLongestSubstring(string s) {
vector<int>sum;
vector<char>str;
for (int i = 0;i < s.length();i++) {
str.push_back(s[i]);
for (int j = i+1;j < s.length();j++) {
for (int c = 0;c < str.size();c++) {
if (s[j] != str[c] && c == str.size() - 1) {
str.push_back(s[j]);
break;
}
if (s[j] == str[c]) {
sum.push_back(str.size());
str.clear();
break;
}
}
if (j == s.length()-1) {
sum.push_back(str.size());
str.clear();
break;
}
if (str.size() == 0)break;
}
if (s.length() == 1)sum.push_back(1);
}
for (int i = 0;i < sum.size();i++) {
if (i == sum.size() - 1)break;
if (sum[i] > sum[i+1]) {
int number = sum[i];
sum[i] = sum[i + 1];
sum[i + 1] = number;
}
}
if (s == " ")sum.push_back(1);
return sum.size()==0?0:sum[sum.size()-1];
}
现在给大家分享大佬的解决办法一共有两种。。。
动态规划!
动态规划:这种算法其实在写这道题的前面我就已经学过了这属于5大常规算法之一,但是我为什么想不到呢?对于这个问题我也思考了一下可能是由于自己做的题目较少等有一定量的题目积累以后会想到吧咱就不在这继续扯和题目无关的了。直接介绍动态规划是什么?动态规划也是一种分治思想,但与分治算法不同的是,分治算法是把原问题分解为若干子问题, 自顶向下,求解各子问题,合并子问题的解从而得到原问题的解。动态规划也是自顶向下把原问 题分解为若干子问题,不同的是,然后自底向上,先求解最小的子问题,把结果存储在表格中, 在求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法效率。现在了解了动态规划那么这道题是怎动态规划的呢?为了方便理解我们就用上面的字符串进行讲解s = "abcabcbb",首先从左到右我们依次把不重复字串依次给划分出s1="a",s2="ab",s3="abc",s4="bca",s5="cab",s6="abc"
s7="cb",s8="b",一共将字符串规划为这几种子情况,那在程序中是怎么规划出来的呢?其实实现出来也不怎么难,首先这里要用到双指针的思想定义两个整数i,j用作数组下标因为string类型可以用数组形式访问,用两个嵌套for循环第一个循环开始位置i=1定位到字符串的第二个字符内层for循环j=0定位到字符串第一个字符,让 i 对应的字符和 j 到 i 对应的字符串也就是s1进行比较如果不同则 i 移动到下一个字符的位置,现在规划出的字符串为s2并计算出s2的长度保存在max中所以经过计算max=2具体计算细节就不在讲解因为很简单,i 移动到第三个字符的位置再与s2进行比较因为不同所以现在规划出的字符串为s3并且计算出长度为max=3,i 移动到第四个字符的位置因为和s3中的a重复所以现在就让 j 的起始位置变为s3中重复字符a的下一个位置也就是j=1,现在就可以得到 i 和 j 之间的字符串也就s4,依次让 i 遍历下去就可以依次的得到字符串s5,s6,s7,s8,并且可以得到相应字符串的长度因为这里 i 是从第二个字符依次往后遍历字符串的所以不会存在漏掉的情况,因为max是和前一个字符串长度进行比较只有大于他才能将值赋值给max,所以最后max的大小就是最大无重复字符串的长度。在这已经将大体思路讲清楚具体代码细节自己琢磨。
代码:
int lengthOfLongestSubstring(string s) {
int size = (int)s.size();
if(size==0){
return 0;
}
int lastLen = 1;
int max = 1;
int curLen = 0;
for(int i=1; i<size; ++i){
curLen = 0;
for(int j=i-lastLen; j<=i-1; ++j){
if(s[j]==s[i]){
curLen = i-j;
break;
}
}
if(curLen==0){
curLen = lastLen + 1;
}
if(curLen>max){
max = curLen;
}
lastLen = curLen;
}
return max;
}
第二种高效的解法:
滑动窗口!
滑动窗口:这种算法我也是第一次听,但是在仔细看了大佬的代码以后其实和上文老生常谈的动态规划差不多一样了也用到了双指针的思想,这里用的是c++中的unordered_map<char,int> map,这个容器的底层实现其实就是哈希表如果看了我前面分享的哈希表那么这里的这个容器就不在陌生,因为哈希表可以快速存取并且可以保存字符以及字符在字符串中的位置并且不能存储相同的字符所以在解决这道题的时候就有很大的便捷之处。
现在就说一下代码实现的具体思路:用一个int left=0 和 for 循环 i=0 定位到字符串的第一个字符位置并将第一个字符保存到哈希表中将该字符在字符串中的位置也保存到哈希表中,i=1 定位到第二个字符的位置与哈希表中的字符进行比较如果不同则也将字符该字符保存到表中长度len=i-left+1,如果相同则取出哈希表中的相同字符并且得到该字符在表中的位置并将 left 定位到该字符的下一个位置也就是left = 字符在字符串中的位置+1,现在的len = i-left 所以思路讲到这就会发现其实就和动态规划解法差不多了如果不理解则说明前面的思路没有看懂。
代码:
int lengthOfLongestSubstring(string s) {
int size = (int)s.size();
if(size==0){
return 0;
}
unordered_map<char,int> map;
int left=0;
int max = 0;
for(int i=0; i < size; ++i){
auto got = map.find(s[i]);
if(got != map.end() && got->second >= left){
left = got->second+1;
}
map[s[i]] = i;
int len = i-left+1;
if(len > max){
max = len;
}
}
return max;
}
这两种解法都很牛逼!!!