3、Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. For "bbbbb" the longest substring is "b", with the length of 1.

给定一个字符串,从里面找出最长的而且不含重复字符的子串,然后输出该子串的长度。

很久不接触这类题,做起来挺吃力的。


一如既往地先上最直接的方法:将字符串每一位都存入HashMap,当存在相同的字符时,取HashMap的长度与之前所记录的子串长度的最大值,然后清空HashMap,并且重新从当前子串中的相同字符的位置开始循环以上步骤。

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap<Character, Integer> hashMap = new HashMap<>();
		int size = 0;
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (hashMap.containsKey(c)) {
				if (size < hashMap.size()) {
					size = hashMap.size();
				}
				i = hashMap.get(c);
				hashMap.clear();
			}
			else {
				hashMap.put(c, i);
			}
		}
		if (size < hashMap.size()) {
			size = hashMap.size();
		}
		return size;
    }
}
这段代码时间复杂度为O(n^3),当字符串的长度比较小的时候还可以,但是当字符串长度增长之后就很麻烦了。LeetCode给了一个三万多的字符串过来,当场就Time Limit Exceeded了。


那么接下来就是优化了。查找了一下资料,突然想起char是以ASCII表取值的,算上所有字符共256个,而我们的目的是要判断一个字符是否在当前子串是唯一的。所以思路如下:

1、记录当前子串的起始点startIndex , maxSize为记录下来的子串最大长度

2、for循环[for( int i = 0 ; i < s.length() ; i++)],当判断到有相同字符时,首先记录当前子串的长度,与maxSize比较取最大值;然后将起始点startIndex设置为当前子串中相同字符位置的后一位

3、重复步骤二直到循环结束


举个例子,以字符串“abcabcbb”为例。


经过for循环,当 i = 3 时,情况如下


此时,代码中判断到第4个字符与当前子串的中第一个字符相同。

因此,首先计算当前子串长度为 :  i - startIndex = 3

然后比较并取最大值保存到maxSize中 : maxSize = Math.max(maxSize  , i - startIndex)

最后,将startIndex置为当前子串中相同字符串的后一位,如下图


就这样,一直循环重复上述步骤,到最后就能得出结果。这样一来,时间复杂度大致是O(n)。

因为char取整型时数值范围为0~255,而且为了方便获取相同字符所在的Index,我用一个长度为256的int数组来保存所有字符的index。首先把数组中所有项都设为-1,如果当前char在数组中所对应的值大于-1,则说明该字符已经存在过;否则,将改字符的index存进去。

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        int[] index = new int[256];
		int size = 0;
		int start = 0;
		int len = s.length();
		for (int i = 0; i < 256; i++) {
			index[i] = -1;
		}
		for (int i = 0; i < len; i++) {
			int c = s.charAt(i);
			if (index[c] != -1) {
				int indexTemp = index[c];
				for (int j = start; j < indexTemp + 1; j++) {
					index[s.charAt(j)] = -1;
				}
				int temp = i - start;
				size = temp > size ? temp : size;
				start = indexTemp + 1;
				index[c] = i;
			}
			else {
				index[c] = i;
			}
		}
		int temp = len - start;
		size = temp > size ? temp : size;
		return size;
    }
}

要注意的是要将已存在的字符之前的所有字符都设置为-1

这段代码的RunTime为6ms~7ms


而另外在网上看到一段更优的代码,思路是一样的,但是更加简洁方便

public int lengthOfLongestSubstring(String s) {
        if(s==null)
            return 0;
	boolean[] flag = new boolean[256];
 
	int result = 0;
	int start = 0;
	char[] arr = s.toCharArray();
 
	for (int i = 0; i < arr.length; i++) {
		char current = arr[i];
		if (flag[current]) {
			result = Math.max(result, i - start);
			// the loop update the new start point
			// and reset flag array
			// for example, abccab, when it comes to 2nd c,
			// it update start from 0 to 3, reset flag for a,b
			for (int k = start; k < i; k++) {
				if (arr[k] == current) {
					start = k + 1; 
					break;
				}
				flag[arr[k]] = false;
			}
		} else {
			flag[current] = true;
		}
	}
 
	result = Math.max(arr.length - start, result);
 
	return result;
}

这段代码的RunTime为 4ms~5ms

我的代码和这段代码相比,一是之前的int数组初始化多耗费了时间,二是boolean数组比int数组要节省空间。

为此,我又优化了一下代码,直接用int数组的默认值0判断是否存在相同字符,为此,需要将字符index全部+1。优化后的代码RunTime为5ms。而这段代码就不放出来了,因为index全部加一之后反而不好理解了。

【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)内容概要:本文介绍了基于IEEE33节点的配电网韧性提升方法,重点研究了移动储能系统的预布局与动态调度策略。通过Matlab代码实现,提出了一种结合预配置和动态调度的两阶段优化模型,旨在应对电网故障或极端事件时快速恢复供电能力。文中采用了多种智能优化算法(如PSO、MPSO、TACPSO、SOA、GA等)进行对比分析,验证所提策略的有效性和优越性。研究不仅关注移动储能单元的初始部署位置,还深入探讨其在故障发生后的动态路径规划与电力支援过程,从而全面提升配电网的韧性水平。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事智能电网、能源系统优化等相关领域的工程技术人员。; 使用场景及目标:①用于科研复现,特别是IEEE顶刊或SCI一区论文中关于配电网韧性、应急电源调度的研究;②支撑电力系统在灾害或故障条件下的恢复力优化设计,提升实际电网应对突发事件的能力;③为移动储能系统在智能配电网中的应用提供理论依据和技术支持。; 阅读建议:建议读者结合提供的Matlab代码逐模块分析,重点关注目标函数建模、约束条件设置以及智能算法的实现细节。同时推荐参考文中提及的MPS预配置与动态调度上下两部分,系统掌握完整的技术路线,并可通过替换不同算法或测试系统进一步拓展研究。
先看效果: https://pan.quark.cn/s/3756295eddc9 在C#软件开发过程中,DateTimePicker组件被视为一种常见且关键的构成部分,它为用户提供了图形化的途径来选取日期与时间。 此类控件多应用于需要用户输入日期或时间数据的场景,例如日程管理、订单管理或时间记录等情境。 针对这一主题,我们将细致研究DateTimePicker的操作方法、具备的功能以及相关的C#编程理念。 DateTimePicker控件是由.NET Framework所支持的一种界面组件,适用于在Windows Forms应用程序中部署。 在构建阶段,程序员能够通过调整属性来设定其视觉形态及运作模式,诸如设定日期的显示格式、是否展现时间选项、预设的初始值等。 在执行阶段,用户能够通过点击日历图标的下拉列表来选定日期,或是在文本区域直接键入日期信息,随后按下Tab键或回车键以确认所选定的内容。 在C#语言中,DateTime结构是处理日期与时间数据的核心,而DateTimePicker控件的值则表现为DateTime类型的实例。 用户能够借助`Value`属性来读取或设定用户所选择的日期与时间。 例如,以下代码片段展示了如何为DateTimePicker设定初始的日期值:```csharpDateTimePicker dateTimePicker = new DateTimePicker();dateTimePicker.Value = DateTime.Now;```再者,DateTimePicker控件还内置了事件响应机制,比如`ValueChanged`事件,当用户修改日期或时间时会自动激活。 开发者可以注册该事件以执行特定的功能,例如进行输入验证或更新关联的数据:``...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值