leetcode 剑指offer面试题48.最长不含重复字符的字符串
题目
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
提示:
s.length <= 40000
解题思路:经典动态规划
先获取字符串长度len,创建长度为len的int数组dp,其中dp[i]的含义是:包含i位置字符在内最长子字符串长度。
探讨一下怎么找到更新dp数组。
- 当i位置字符在之前没出现过,则可以将i位置字符直接插入到前个包含i-1位置字符串的尾部,即dp[i]=dp[i-1]+1
- 当i位置字符在之前出现过,则计算当前字符与上一个相同最近字符的位置距离distance。
(1)若distance>dp[i-1],说明在包含i-1位置字符在内的那个最长子字符串内没有当前字符,可以将当前字符插入到前个包含i-1位置字符的字符串尾部,即dp[i]=dp[i-1]+1。
(2)若distance<=dp[i-1],说明在包含包含i-1位置字符在内的那个最长子字符串内存在当前字符,所以不能直接插入到包含上个字符的字符串尾部,而是将当前从上个相同字符的下一位开始直到当前i位置字符组成新的字符串,即dp[i]=distance。
状态转移方程:
dp[i]=dp[i-1]+1 ,当i位置字符没出现过或出现过且distance>dp[i-1]。
dp[i]=distance ,当i位置字符出现过且distance<=dp[i-1]。
这里还有个问题:为了找到包含该字符在内最长子字符串长度,我们需要知道这个字符在当前位置之前是否出现过。有两种解决办法,一种是从当前位置i开始往前遍历,直到找到最近的当前字符;第二种方法是创建一个数组,用来记录当前字符在上次出现的位置,并每次更新。我选择用空间换取时间选择了方法二。
最后返回dp数组中的最大值。
代码如下:
int lengthOfLongestSubstring(string s) {
if(s.empty()) return 0;
int len=s.size();
//dp意义:包含当前字符在内的最长字符串长度
vector<int> dp(len);
dp[0]=1;
vector<int> prevposition(128,-1);
int maxLength=1;
prevposition[s[0]]=0;
for(int i=1;i<len;i++){
//当前字符对于位置
int index=s[i];
//当前字符之前没出现过
if(prevposition[index]==-1){
dp[i]=dp[i-1]+1;
}
else{//之前出现过
//前个当前字符出现的距离
int distance=i-prevposition[index];
if(distance>dp[i-1]){//两个相同字符的距离大于包含前个字符串的最长长度
dp[i]=dp[i-1]+1;//则前个字符串必没有当前字符,直接套在后面
}
else{//若两个相同字符的距离不大于,则不能把当前字符套在包含前个字符串的后面,只能重新变成新的从上个当前字符的下一个开始到当前字符为最长字串
dp[i]=distance;
}
}
maxLength=max(maxLength,dp[i]);
prevposition[index]=i;
}
return maxLength;
}