题目如下:
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.
解法1:
一开始看到题目后,由于题目中并没有给出数据范围,我直接就采用暴力法进行求解。
首先,创建一个数组,用于记录字符串中的某个字母是否在前面的子串中已经出现。
接着,开始遍历整个字符串。假设我们已经遍历到字符串的第i位,则接下来进入第二重循环,从第i+1位开始继续遍历字符串,每次再查询一遍当前截取的子串中有没有重复字母出现。当子串中出现重复字母,则跳出循环,更新最大不含重复字母的子串的长度。
对应代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if (s == "")
return 0;
int length = 1;
for (int i = 0; i < s.length(); i++)
{
int tlength = 1;
for (int j = i + 1; j < s.length(); j++)
{
for (int k = i; k < j; k++)
{
if (s[k] == s[j])
goto next;
}
tlength++;
}
next:
if (tlength > length)
length = tlength;
}
return length;
}
};
提交后,果不其然,超时了。这个算法的时间复杂度为O(n^3),很明显,需要进一步优化。
解法2:
在解法1的基础上,很容易就能找到一种O(n^2)的算法。在查询子串中是否有重复字母出现时,我们可以用一个数组来记录一个字母是否已经出现过,这样便可以减少一重循环。
首先,我们创建一个数组,数组大小我直接设置为1000,保证能存入每个字母的ASCII值,整个数组的初始化值为0。
接着,开始遍历字符串,假设我们已经遍历到第i位,则接下来进入第二重循环,从第i位开始继续遍历字符串。如果遍历过程中,某个字母已经被标记值为1,则停止第二重循环,并将记录字母是否出现的数组初始化值为0。否则,继续增加子串长度,并将该字母标记值为1。
对应代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if (s == "")
return 0;
int f[1000];
for (int i = 0; i < 1000; i++)
f[i] = 0;
int length = 0;
int tlength = 0;
for (int i = 0; i < s.length(); i++)
{
tlength = 0;
for (int j = i; j < s.length(); j++)
{
if (f[s[j]] != 0)
{
for (int i = 0; i < 1000; i++)
f[i] = 0;
break;
}
f[s[j]] = 1;
tlength++;
}
if (tlength > length)
length = tlength;
}
return length;
}
};
解法3:
在做题过程,有朋友看到我正在写这道题,他提出有一种O(n)的算法。
首先,同样的,我们需要创建一个数组。但在这个算法中,该数组用于记录每个字母出现的位置。此外,我们还需要一个变量x,用于记录子串开始的位置。
接着,遍历整个字符串。如果遍历过程中,没有重复字母出现,则每次记录下该字母出现的位置,并更新最大子串长度。如果出现了一个重复字母,则查找该字母出现的上一个位置,并把变量x设置为该位置的下一位,表示下一个新的子串的起始位置。
对应代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int f[1000];
for (int i = 0; i < 1000; i++) f[i] = 0;
int mm = 0;
int x = 0;
for (int i = 0; i < s.length(); i++)
{
if (f[s[i]] != 0)
{
int X = f[s[i]] - 1;
while (x <= X)
{
f[s[x]] = 0;
x++;
}
}
f[s[i]] = i + 1;
mm = max(mm, i - x + 1);
}
return mm;
}
};
从上图中可以看到,三种算法的对应运行时间的差距。