计算不含重复字符的最长子串的长度 #算法#

25 篇文章 1 订阅

原题如下:

Given a string, find the length of the longest substring without repeating characters.
给出一个字符串,计算没有重复字符的最长子串的长度。

Example 1:

  Input: "abcabcbb"
  Output: 3 
  Explanation: The answer is "abc", with the length of 3. 

Example 2:

  Input: "bbbbb"
  Output: 1
  Explanation: The answer is "b", with the length of 1.

Example 3:

  Input: "pwwkew"
  Output: 3
  Explanation: The answer is "wke", with the length of 3. 
         Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

思路

从左向右扫描,如果下一字符在之前没有出现过,则继续下去,直到一个重复字符的出现,计算到这里之前的子串的长度,然后继续从该位置向右扫描,继续寻找是否有更长的符合条件的子串,但是下一子串的开头就必须从刚才那个重复字符出现过的位置的下一位置开始。

比如abcad,一开始依次扫描abc,然后到a的时候发现重复了,于是计算当前子串abc长度为3,继续刚才的扫描,下一字符是d,然后结束;因为第一次的时候a是重复字符,所以计算第二个子串长度时应该从b开始,即bcad,长度为4,比刚才的3更长,所以最终结果为4。

想法是这样,但是怎么实现也是个问题。判断字符是否出现过,可以用一个128位(或256位)的数组num[],字符可以对应ASCII中的0~127,数组相应位置的元素用来标识是否出现过,比如可以用num[‘a’]=1表示其已经出现过。但是这样会带来问题,就是如何在识别下一个子串时恢复所有字符的状态,还有如何计算子串的长度。

一种方式是数组对应元素记录该字符在子串中的位置,并在每次遇到一个新子串时记录长度,并更新位置。
代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
    	int num[256] = {0};
    	int len = 0;
    	int maxLen = 0;
    	for(auto i : s){
    		if(num[i] != 0){
    			if(maxLen < len) maxLen = len;
    			len = len - num[i];
    			for(int j = 0; j < 256; j++){
    				if(num[j] != 0 && j != i){
    					int order = num[j] - num[i];
    					num[j] =  order > 0 ? order : 0;
					}
				}
			}
			len++;
			num[i] = len;
		}
        return maxLen >= len ? maxLen : len;
    }
};

改进

上述的方法需要在每次遇到新子串都更新一遍数组,这样很影响性能,一个好的改进就是数组记录对应字符最近出现的位置,并用一个变量subStart记录子串开始位置,若字符最近出现的位置在subStart的右边,说明已经重复。
代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
    	int maxLen = 0;
    	int index[128] = {0}; // 记录最近下标的下一位置 
    	for(int i = 0, subStart = 0; i < s.size(); i++){
    		char a = s[i];
			subStart = max(index[a], subStart);
			maxLen = max(maxLen, i - subStart + 1);
    		index[a] = i + 1;
		}
		return maxLen;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值