LeetCode Q3

一天一题的感觉还是不错的,今天要解决的是第三题,要求是求字符串中不重复的最长子串的长度,原题如下:

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.

很显然,此题和所有搜索问题一样可以采用穷举法,从头到尾遍历字符串,将每个字符与前面的字符比较,未出现的字符保留,出现过的自动跳过,此法复杂度为O(n²),显然有很多冗余。

按常规逻辑说,为了解决冗余,应该从重复比较入手或者利用空间换取时间,但是此题的冗余没什么规律,不好剔除,于是我想先提一个脑洞比较大的算法,就是把字符串按ASCII码排序,然后再遍历,利用标准库自带的快速排序算法将时间复杂度降低至小于O(nlogn),代码很好实现,所以我就不写了。

哈哈,让我们还是按照常规逻辑来,试试利用空间换取时间,那如何利用空间换取时间呢?研究穷举法发现,算法时间大量花在比较该元素与先前元素是否相同上,即判断当前元素是否出现过,如果我们能够记录下每次出现过的元素,让访问当前元素时就能够直接知道该元素是否出现过,那么时间就能大大节省下来,从而使得只需遍历一遍便能够完成求最长子串的目的,因而,我们需要构建一个能够记录所有字符(256个)出现与否的数组以备查阅(数组的查询速度很快,为O(1)),在每个元素首次出现后,将相应位置标签置为1或true,同时对长度变量进行增1即可,对于已经为true的长度变量不增加。有了这个思想后,剩下的就简单了,不过要注意不要漏掉空字符串等特殊情况。具体代码如下:

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;



int lengthOfLongestSubstring(string s) {

	int n = s.length();
	bool exist[256];
	for(int i = 0; i<256;i++)exist[i]=false;
	const char* str = s.c_str();
	int len = 0;
	for(int i=0;str[i]!=NULL;i++){
		int strAsc = (int)((unsigned char)str[i]);
		if(exist[strAsc] == false){len++;exist[strAsc] = true;}
	}
	
	return len;
}



int _tmain(int argc, _TCHAR* argv[])
{
	string s = "pwwkew";
	int length = lengthOfLongestSubstring(s);
	cout<<"the length of the string is "<<length<<endl;
	return 0;
}

结果LeetCode说wrong answer了。。。并给出例子:"pwwkew"应该输出3而不是4。。想了半天都不明白,后来百度了最长子串的定义,我才反应过来,最长子串要求相邻字符构成,那是不是检测到重复字符然后去掉就结束了呢,当然也不是,因为本题要求最长,因此剔除完(遍历至最后一个重复元素)后,还要判断之后的字符串是否存在更长的不重复字串,故遍历需持续到串尾,同时要不断更新最长子串的长度值。故遍历所需指针/下标需要有2个,遇到重复字符后开始分别从重复的两处开始遍历元素,找到相同的子串,然后减去这些重复部分,再继续遍历得到最终的最长不重复字串。修改程序如下:

int lengthOfLongestSubstring(string s) {

	int n = s.length();
	bool exist[256];
	for(int i = 0; i<256;i++)exist[i]=false;
	const unsigned char* str = (unsigned char*)s.c_str();
	int len = 0;
	int i = 0,j = 0;
	while(i<n){
		if(exist[str[i]] == false){
			exist[str[i]] = true;
			i++;
		}
		else{
			len = max(len,i-j);
			while(str[j] != str[i]){
				exist[str[j]] = false;
				j++;
			}
			i++;
			j++;
		}
	}
	len = max(len,n-j);
	return len;
}
这里要强调的一点:

为什么要在找相同元素位置的时候将不同的元素视为没出现过(即为什么要写下述代码)?

exist[str[j]] = false;

因为在出现重复元素以后还要继续寻找是否存在更长的不重复子串,相当于又开启了一个新的寻找过程,因而先前出现过的已经不能再视为存在,而重复元素不置为false的原因在于新的寻找过程是从重复字符开始的。

最后,代码顺利通过了。

总结:

1.对于字符串的处理,首先要明确相关概念,其次要掌握一些基本思想和算法,比如KMP、本题、还有DP中比较经典的最长公共子串等。这里给大家推荐一篇别人的关于字符串经典算法的文章:http://blog.csdn.net/han_xiaoyang/article/details/11969497#t15,文中有关于此题更多更好的算法。

加油!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值