LeetCode题中第三题,中等难度
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。
解法一:动态规划算法
根据动态规划算法的定义,以下这种解法与之类似,但并不是很确定属于动态规划算法范围。
前期准备:
- 定义一个数组用来存储计计算完成后子串的值
- 确定初始条件,dp[0] = 0用来对数组进行初始化也为数据的取值打下基础
分析:
- 题目要求的是获取字符串的最长子串长度,所以获取到的子串是一个动态的字符串。
- 首先,尝试使用暴力破解法的思路进行求解,暴力破解法每次只能获取到字符串指定位置上的字符,进行的比较只能判断当前位置与起始位置的值是否相等,所以此种方法是不可行的。
- 尝试使用动态规划法的思路:将计算过的的值存储起来,在下此次需要计算相同的值时直接拿来用。为保证计算的值能够充分利用,计算顺序我们可以采用从小到大的顺序,保证需要计算一个值时所关联的另一个值已经计算出
- 思路:字符串从左往右的顺序进行计算,我们所需要的子串不再向往往常一样确定一个值从左往右去比较是否有相同的字符。我们反其道而行,在判断子串时,从右往左判断前面是否有与它其中字符相同的字符。
- 在获取到某个位置的字符,判断它之前是否存在与该字符中相同的字符,有的话就使用当前串的这个字符的位置减掉相同字符的值,得到的就是子串长度。
- 使用map数组来记录字符出现的位置,而由于我们的计算是从左往右的,当有相同字符出现,我们计算所使用的是更靠右边的字符,所以这个字符对应的位置值需要进行更新。
- 我们定义的数组dp中保存子串以这个字符结束的长度。
- 最后将dp数组遍历取最大值即可
if(s.length() == 0) return 0; //用来保存每一个字符及它的位置 Map<Character,Integer> map = new HashMap<>(); //创建数组,用来保存字符串每个位置开始到前端的最大无重复字符串字数 int[] dp = new int[s.length() + 1]; dp[0] = 0; for(int i = 1; i < s.length()+1 ; i++){ //s.charAt(i)方法的索引i是从0开始的,由于dp[0]已经被定义赋值了所以下标从1开始 if(map.containsKey(s.charAt(i-1))){ dp[i] = Math.min(i - map.get(s.charAt(i-1)),dp[i-1] + 1); //计算子串是从选定字符从后往前计算的,将之前的重复字符替换 map.replace(s.charAt(i-1),i); } else { map.put(s.charAt(i-1),i); dp[i] = dp[i-1] + 1; } } int intMax = 0; for(int i = 1; i < dp.length; i++){ intMax = Math.max(intMax,dp[i]); } return intMax;
解法二:滑动窗口
滑动窗口:定义两个指针(start,end),两指针之间的值就类似于一个窗口,遍历数据时,start指针向前移动,直至满足一定条件时,start指针停止移动,end指针开始向前移动,获取到最终结果,然后end指针向前移动,开始下一次运行。(我也说不清,问度娘吧)
思路:
- 遍历字符串,定义两个指针,皆指向起始位置。两个指针所指向的字符与其中间字符连接起来就是字符串的子串,计算出字符串的长度并保存,在下一个长度获取到时与之比较去较大值。
- 两指针的移动方式为:首先end指针向前移动,将指针所指向的字符和它在字符中的位置存储到map中。当end指针所指向的字符在map中有相同的字符时,更新start的位置为上个重复字符的下一位,同时比较最大长度。
int intMax = 0; //定义map用来记录字符及以该字符结束的无重复子串长度 Map<Character, Integer> map = new HashMap<>(); for (int end = 0, start = 0; end < s.length(); end++) { char element = s.charAt(end); if (map.containsKey(element)) { start = Math.max(map.get(element), start); } intMax = Math.max(intMax, end - start + 1); map.put(s.charAt(end), end + 1); } return intMax;