题目描述
从字符串中找出一个最长的不包含重复字符的子字符串,计算这个字符串长度。字符串只包含a-z字母。如“arabcacfr”,最长的包含重复子字符串是“acfr”,长度是4。
- 解题思路:
已知第i-1个字符的最长子字符串长度,根据第i个字符是什么,总结规律,得出第i个字符的最长子字符串长度。 - 几个注意点:
1.递归用循环代替减少重复计算。 - 解题步骤:
先推递归式:
一开始没看懂是因为看错了f(i)的定义。强调下f(i)是以第i个字符为结尾的不包含重复字符的子字符串的最长长度。- 如果第i个字符没出现过,f(i)=f(i-1)+1
- 如果第i个字符出现过,得到上次出现的位置的距离d
- 如果d小于等于f(i-1),说明上次出现的位置在f(i-1)那个子字符串之中,调整f(i)=d
- 如果d大于f(i-1),说明上次出现的位置在f(i-1)那个子字符串之前,f(i)=f(i-1)+1
获取上次出现过的字符的位置,用一个数组position来维护,实时更新数组状态。
但我们要求的不是f(i),我们要维护一个maxLength变量,用来更新递归/循环过程中的最大值,也就是我们要求的答案。
如果没遇到出现过的字符,没必要更新maxLength,在循环结束后最后更新maxLength=f(i)就行。
如果遇到过的字符,并且要修改了f(i)=d之前,一定要更新maxLength,因为修改后的f(i)可能不是最大的长度了。
最后返回maxLength,即为最后答案。
【考点】递归 动态规划
class Solution {
public:
int longestSubstringWithoutDuplication(const string& str) {
int curLength = 0; // f(i)
int maxLength = 0; // 答案
int* position = new int[26];
for (int i = 0; i < 26; ++i)
position[i] = -1;
for (int i = 0; i < str.length(); ++i)
{
int prevIndex = position[str[i] - 'a'];
int d = i - prevIndex;
// 字符没出现过 或者 字符出现过但d > f(i-1)
if (prevIndex < 0 || d > curLength)
// f(i) = f(i-1)+1
++curLength;
else // 字符出现过但d <= f(i-1)
{
// 更新答案
if (curLength > maxLength)
maxLength = curLength;
// f(i) = d
curLength = d;
}
position[str[i] - 'a'] = i;
}
// 更新答案
if (curLength > maxLength)
maxLength = curLength;
delete[] position;
return maxLength;
}
};
int main(){
Solution obj;
string str= "arabcacfr";
int res = obj.longestSubstringWithoutDuplication(str);
cout << res << endl;
return 0;
}
Output
4