最近终于拾起了久违的编程,开始做leetcode上的题,开始看啊哈磊的《啊哈!算法》,忽然觉得算法其实是很有意思的,当然这是建立在会编程的基础之上的,所以我现在致力于学好C++和编程,也许有一天我也可以成为大神呢哈哈~不得不承认我真的是太菜,需要学习的东西还有很多,抱大牛们大腿啊。。由于数据结构知识不牢固,导致对于最简答的题目都想不到好的解决方法,可是我已经开始在不断地进步和学习了,每天学习一点,会有收获的~我要开始坚持写CSDN博客了,看到大牛们的高深莫测,让我更有动力去学习!
最近接触排序的算法,发现了一个很好用的思路,就是充分利用数组的下标来标记元素是否出现过,排序中的桶排序就是这个原理,需要注意的是,只要涉及到去重或者无重复这样的关键词,就要想到用哈希表(如leetcode第一题)或者是标记法。
leetcode上的第三题让我受益匪浅,我学习到了很多不同的解决方法。
思路一:最直接的枚举法。从所给字符串中找出所有的子串,然后判断每一个子串是否含有相同的元素即可。
class Solusion{
public:
int lengthOfLongestSubstring(string s){
int i = 0, j = 0, k = 0;
int len = s.length();
int maxlen = 0;
string tempstr = "";
for (i = 0; i < len; i++){
for (j = i+1; j <= len; j++){
//获得从i到j的子字符串,一共有n*(n+1)/2个子字符串
tempstr = s.substr(i, j-i);
//判断子串中是否有重复出现的元素
bool exist[256] = {false};
bool repeat = false;
for (k = 0; k < tempstr.length(); k++){
//如果字串中存在重复元素
if (exist[tempstr[k]] == true){
repeat = true;
break;
}
exist[tempstr[k]] = true;
}
//如果当前处理的子串没有重复字符
if (!repeat){
if (tempstr.length() > maxlen){
maxlen = tempstr.length();
}
}
}
}
return maxlen;
}
};
这个代码在leetcode上能够正常运行,一个网友写的代码中有个错误,现已改正:
k < tempstr.length();
在提交代码的时候,出现错误Time Limit Exeeded,该算法的时间复杂度是O(n^3),算法复杂度太高。
思路二: 用一个数组(256个)来标记元素是否出现,用两个指针 i,j 来表示子串的起始和终止位置,这也是leetcode官网给出的答案,算法时间复杂度是O(n)。这里值得注意的一个问题是,最后一次比较不要忘记,因为在 j 非0的时候,i - j 并不是子串的长度。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
要查找的字符串 | a | b | c | d | c | a | b | c | d | e | |
i , j 初始位置 | i, j | ||||||||||
无重复元素时 i 往前移动 | j | i -> | |||||||||
第一次查到重复元素位置 | j | i | |||||||||
此时 j 移动到重复元素位置 | j | i | |||||||||
i, j 同时往前移动一个位置 | j | i | |||||||||
如此反复 | j | i | |||||||||
j | i | ||||||||||
注意:最后一次也是需要比较的 | j | i |
class Solution{
public:
int lengthOfLongestSubstring(string s){
if (s.empty())
return 0;
int len = s.size();
int bitmap[256] = {0};
int maxlen = 0;
int j = 0;
for (int i = 0; i < len; i++){
if (bitmap[s[i]] == 0){
bitmap[s[i]] = 1;
}
else{
maxlen = max(maxlen, i - j);
//对bitmap进行设置,将重复元素之前的所有元素对应的位置设为0,因为这些元素不参与下一次子串
while (s[i] != s[j]){
bitmap[s[j]] = 0;
j++;
}
j++;
}
}
maxlen = max(maxlen, len - j); //注意最后还要判断一下
return maxlen;
}
};
思路三: 网友的一种巧妙的解决方案值得思考,就是首先将数组(256个)的每个元素设为-1,idx表示当前子串的起始位置的前一个位置的下标,算法复杂度也是O(n)。(反正这个解法我是想不出来。。至今还在感叹某人的聪慧。。)直接放上大神的代码和自己的一点理解吧~
class Solution{
public:
int lengthOfLongestSubstring(string s)
int locs[256]; //标记元素是否出现过
memset(locs, -1, sizeof(locs));
int idx = -1, max = 0; //idx为当前子串的起始位置的前一个位置
for (int i = 0; i < s.size(); i++){
//如果当前元素出现过,那么当前子串的起始位置为这个字符上一次出现的位置+1
if (locs[s[i]] > idx){
//新子串的起始位置设为s[i]出现的位置+1
//idx为当前子串的起始位置的前一个位置的下标
idx = locs[s[i]];
}
//如果当前子串的长度大于最大值
if (i - idx > max){
max = i - idx;
}
//更新元素s[i]出现的位置
locs[s[i]] = i;
}
return max;
};
这是我总结的一些思考方法,希望自己以后回头看现在的代码时,会有种醍醐灌顶的感觉。
谢谢~我会继续努力的~