无重复字符的最长子串【动态规划解法】

题目描述

给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。

样例

> 输入: "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记录在状态转移过程中,某字符出现的最末位置。
不难推导出状态转移存在三种情况:

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[i1]+1

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[i1]<(i1)lastindex[i]+1,此时依旧为 d p [ i ] = d p [ i − 1 ] + 1 dp[i]=dp[i-1]+1 dp[i]=dp[i1]+1;

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[i1](i1)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]=ilastindex[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[i1]+1ilastindex[s[i]]ifelses[i]isntinlastindexordp[i1]<ilastindex[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)

扫描二维码关注公众号,回复: 11903138 查看本文章

优化后的代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/RealCoder/article/details/108566720
今日推荐