题目:请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。假设字符串中只包含从’a’到’z’的字符。例如,在字符串"arabcacfr"中,最长的不含重复字符的子字符串是"acfr",长度为4。
思路:
方法一:暴力法,时间复杂度为O(n3)
一个长度为n的字符串有O(n2)个子字符串,我们找出所有的子字符串,然后需要O(n)判断每个子字符串中是否包含重复的字符,总的时间复杂度为O(n3)
方法二:动态规划法,时间复杂度为O(n)
首先定义函数f(i)表示以第i个字符为结尾的不包含重复字符的子字符串的最长长度,接着从左到右逐一扫描字符串中的每个字符。因为当我们计算以第i个字符为结尾的不包含重复字符的子字符串的最长长度f(i)时,我们已经知道f(i-1)了。具体分析如下:
扫描到第i个字符 | f(i) |
---|---|
第i个字符之前没出现过 | f(i)=f(i-1)+1 |
第i个字符之前出现过,d>f(i-1) | f(i)=f(i-1)+1 |
第i个字符之前出现过,d<=f(i-1) | f(i)=d |
两种方法的核心代码如下:
//方法1:暴力法,时间复杂度为O(n3)
bool hasDuplication(const string& str, int position[]);
int longestSubstringWithoutDuplication1(const string& str){
int longest = 0;
int* position = new int[26];
//一个长度为n的字符串有O(n*n)个子字符串
for(int start = 0; start < str.length(); start++){
for(int end = start; end < str.length(); end++){
int count = end - start + 1;
const string& substring = str.substr(start, count);
if(!hasDuplication(substring, position)){ //如果substring中不包含重复字符
if(count > longest)
longest = count;
}
else
break;
}
}
delete[] position;
return longest; //返回最长子字符串的长度
}
//检测子字符串中是否包含重复字符,如果包含则返回true,如果不包含返回false
bool hasDuplication(const string& str, int position[]){
//position数组初始化为-1,表示每个元素对应的字符在字符串中还没有出现过
for(int i = 0; i < 26; i++)
position[i] = -1;
for(int i = 0; i < str.length(); i++){
int indexInPosition = str[i] - 'a';
if(position[indexInPosition] >= 0) //当前字符在前面已出现过
return true;
position[indexInPosition] = indexInPosition;
}
return false;
}
//方法2:动态规划法,时间复杂度为O(n)
int longestSubstringWithoutDuplication2(const string& str){
int curLength = 0;
int maxLength = 0;
int* position = new int[26];
//position数组初始化为-1,表示每个元素对应的字符在字符串中还没有出现过
for(int i = 0; i < 26; i++)
position[i] = -1;
for(int i = 0; i < str.length(); i++){
int prevIndex = position[str[i] - 'a']; //将第i个字符上次出现在字符串中的位置存入prevIndex中
//该字符从未出现过 或 第i个字符和它上次出现在字符串中的位置的距离(i - prevIndex)大于curLength,即d>f(i-1)
//意味着第i个字符上次出现在f(i-1)对应的最长子字符串之前,因此仍然有f(i)=f(i-1)+1
if(prevIndex < 0 || i - prevIndex > curLength)
curLength++; //令f(i)=f(i-1)+1
else{ //d<=f(i-1),此时第i个字符上次出现在f(i-1)对应的最长子字符串中,因此f(i)=d
if(curLength > maxLength)
maxLength = curLength;
curLength = i - prevIndex; //令f(i)=d
}
position[str[i] - 'a'] = i; //position用来存储每个字符最近一次出现在字符串中位置的下标
}
if(curLength > maxLength)
maxLength = curLength;
delete[] position;
return maxLength;
}