题目描述
给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。
样例
> 输入: "abcabcbb"
> 输出: 3
> 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
思路
动态规划
设字符串为 s s s,定义 d p [ i ] dp[i] dp[i]为以 s [ i ] s[i] s[i]为结尾的字符串的无重复字符的最长子串,定义映射map <char ,int>lastindex
记录在状态转移过程中,某字符出现的最末位置。
不难推导出状态转移存在三种情况:
1° s [ i ] s[i] s[i]未在之前状态出现过,即 l a s t i n d e x lastindex lastindex中找不到 s [ i ] s[i] s[i],则 d p [ i ] = d p [ i − 1 ] + 1 dp[i]=dp[i-1]+1 dp[i]=dp[i−1]+1;
2° s [ i ] s[i] s[i]出现过,但是不包含在dp[i-1]对应的最大的子串中,数学表达为 d p [ i − 1 ] < ( i − 1 ) − l a s t i n d e x [ i ] + 1 dp[i-1]<(i-1)-lastindex[i]+1 dp[i−1]<(i−1)−lastindex[i]+1,此时依旧为 d p [ i ] = d p [ i − 1 ] + 1 dp[i]=dp[i-1]+1 dp[i]=dp[i−1]+1;
3° s [ i ] s[i] s[i]出现过,但是包含在dp[i-1]对应的最大的子串中,数学表达为 d p [ i − 1 ] ≥ ( i − 1 ) − l a s t i n d e x [ i ] + 1 dp[i-1]≥(i-1)-lastindex[i]+1 dp[i−1]≥(i−1)−lastindex[i]+1,此时应有 d p [ i ] = i − l a s t i n d e x [ s [ i ] ] dp[i]=i-lastindex[s[i]] dp[i]=i−lastindex[s[i]]
即 d p [ i ] = { d p [ i − 1 ] + 1 i f s [ i ] i s n ′ t i n l a s t i n d e x o r d p [ i − 1 ] < i − l a s t i n d e x [ s [ i ] ] i − l a s t i n d e x [ s [ i ] ] e l s e dp[i]=\begin{cases} dp[i-1]+1 &if &s[i]&isn't &in &lastindex& or&dp[i-1]<i-lastindex[s[i]]\\ i-lastindex[s[i]]& else\end{cases} dp[i]={ dp[i−1]+1i−lastindex[s[i]]ifelses[i]isn′tinlastindexordp[i−1]<i−lastindex[s[i]]
最终得到答案 m a x max max{ d p [ i ] dp[i] dp[i]}
初步代码
#include<iostream>
#include<map>
#include<string>
using namespace std;
map<char, int> lastindex;
int lengthOfLongestSubstring(string& s) {
int *dp = new int[s.size()];
int ans = 0;
dp[0] = 1;
for (int i = 1; i < s.size(); ++i) {
if (lastindex.find(s[i]) == lastindex.end() || dp[i - 1] < i - lastindex[s[i]]) dp[i] = dp[i - 1] + 1;
else dp[i] = i - lastindex[s[i]];
lastindex[s[i]] = i;
if (ans < dp[i]) ans = dp[i];
}
delete[] dp;
if (s.size() == 1) return 1;
return ans;
}
int main()
{
string s = "cabbcac";
cout << lengthOfLongestSubstring(k) << endl;
return 0;
}
分析与优化
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( k + n ) O(k+n) O(k+n), k k k为字符种类数量, k < = 128 k<=128 k<=128, n n n为字符串长度
考虑到dp【i】
最多与dp【i-1】
有关,可采用滚动数组,将空间复杂度降至 O ( k + 2 ) O(k+2) O(k+2)
优化后的代码:
#include<iostream>
#include<map>
#include<string>
using namespace std;
map<char, int> lastindex;
int lengthOfLongestSubstring(string& s) {
int dp = 0, ans = 0;
for (int i = 0; i < s.size(); ++i) {
if (lastindex.find(s[i]) == lastindex.end() || dp < i - lastindex[s[i]]) ++dp;
else dp = i - lastindex[s[i]];
lastindex[s[i]] = i;
if (ans < dp) ans = dp;
}
return ans;
}
int main()
{
string s = "cabbcac";
cout << lengthOfLongestSubstring(s) << endl;
}