[剑指offer]最长不含重复字符的子字符串
题目描述
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
提示:
s.length <= 40000
解题思路
- 暴力解法,算上查找,时间复杂度为O(n3),空间复杂度O(1)
- 动态规划
- 状态定义:动态规划列表dp,dp[j]为字符s[j]结尾的最长不含重复字符的子字符串
- 转移方程:固定右边界j,字符s[j]左边距离最近的相同字符为s[i],即s[j]=s[i]。当i<0,即s[j]左边没有相同字符,则dp[j]=dp[j-1]+1;当dp[j-1]<j-i,即s[i]在dp[j-1]子字符串之外,则dp[j]=dp[j-1]+1;当dp[j-1]>=j-i,即s[i]在dp[j-1]子字符串之内,则dp[j]=j-i。当i<0时,dp[j-1]<j-i恒成立,因此可以总结为:
- dp[ j ] = dp[ j - 1 ] + 1(当dp[ j - 1 ] < j - i)
- dp[ j ] = j - i(当dp[ j - 1] >= j - i)
- 所以,问题是,遍历字符s[j]时,如何确定索引 i
- 线性遍历
- 哈希表
实现代码
//暴力解法
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
if(len==0)
return 0;
int maxcount=1;
int i=0;
while(i<len-1){
int start=i; //开始的位置
string rs;
rs+=s.at(i);
int count=1;
for(int j=i+1;j<len;j++){
int pos=rs.find(s.at(j));
if(pos!=-1){
i=start+pos+1;//下一次开始的位置,假如pwewekw,在i=0遍历到j=3时,会在pwe中找到w,那么下一次就可以从i=2开始
break;
}
else{
rs+=s.at(j);
count++;
i++;
}
}
maxcount=max(maxcount,count);
}
return maxcount;
}
};
//动态规划-线性遍历1
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
if(len==0)
return 0;
vector<int> dp(len);
dp[0]=1;
for(int j=1;j<len;j++){
int i=j-1;
while(i>=0){//确定i的位置
if(s[i]==s[j])
break;
i--;
}
if(dp[j-1]<j-i)
dp[j]=dp[j-1]+1;
else
dp[j]=j-i;
}
int max=0;
for(int i=0;i<len;i++){//找dp最大值
max=max>dp[i]?max:dp[i];
}
return max;
}
};
时间复杂度为:O(n2)
空间复杂度为:O(n)
可以继续优化空间复杂度,因为返回值是dp列表的最大值,那么就可以用一个dp变量存储dp[j],变量max每次取最大值,就可以省去dp列表使用的O(n)的额外空间
//动态规划-线性遍历2
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
if(len==0)
return 0;
int dp=1;
int max=dp;
for(int j=1;j<len;j++){
int i=j-1;
while(i>=0){
if(s[i]==s[j])
break;
i--;
}
if(dp<j-i)
dp=dp+1;
else
dp=j-i;
max=max>dp?max:dp;
}
return max;
}
};
时间复杂度:O(n2)
空间复杂度:O(1)
可以继续优化时间复杂度,使用哈希表存储各字符最后一次出现的位置,可以节省遍历查找s[i]的O(n)时间,而字符的 ASCII 码范围为 00 ~ 127127 ,哈希表最多使用 O(128) = O(1)大小的额外空间。
//动态规划-哈希表
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
if(len==0)
return 0;
map<char,int> mymap;
for(int i=0;i<len;i++)
mymap[s[i]]=-1;//初始化为-1,否则没有mymap[s[j]]时会默认创建并赋值0
int dp=1;
int max=dp;
mymap[s[0]]=0;
for(int j=1;j<len;j++){
int i=mymap[s[j]];
if(dp<j-i)
dp=dp+1;
else
dp=j-i;
max=max>dp?max:dp;
mymap[s[j]]=j;
}
return max;
}
};
时间复杂度:O(n)
空间复杂度:O(1)