Q:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串 。
先用我自己的方法写了一下这道题:
主要是要考虑一些细节的地方:
先举个例子:shsiasyehdbxjxjsg
它可以分成以下几段:
sh
siasyehdbxj
ia
xjsg
1.我本来想的是如果遇到前面出现过的字符,就在这断掉,然后重新开始新的字符,比如sia,syehdbxj,这样最长的子串长度是 syehdbxj.size()。但是我没有考虑如果前面加上 ia,也符合无重复子串的长度,这样最长子串长度就是iasyehdbxj.size()。这个是需要注意的一个地方。PS:这里要感谢晚上11.30还在陪我刷题的男朋友给我找出的问题。
2.如果遇到连续重复的字符,和遇到之前出现过的字符处理是不同的,所以我放在else if{} else{}里,分两段写了。
3.最后我把所有分出来的几段字符长度都放在一个vector数组里,本来还想写个从里面找最大值的函数。后来灵机一动直接用sort排序,然后最后一个数肯定就是最大值了。
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
if (s.empty())return 0;
if (s.size() == 1)return 1;//只有一个字符就直接返回1
char temp = s[0];
string s1;
vectorresult;//记录不同的子串的长度。其实也可以用两个计数器,一个用来计数,另一个用来存放之前的个数,然后两个比较,留下大的那个。不知道会不会比创建vector快一点。明天试一下。
int count = 1, last;
s1.push_back(s[0]);
for (int i = 1; i
尝试了上面代码里备注所说的计数问题,看看会不会对运行时间产生影响。加了一句:
count2=count>count2?count:count2;//count2 用来保存子串长度的最大值。
最后时间并没有什么改变,说明了主要的时间耗费,可能还是在for循环和各种比较上。
看一下官方给出的解决方法:
1.暴力法:
列举出来所有的子字符串,然后通过一个bool函数检查每个子字符串中有没有重复的字符,如果没有,就返回true ,再找到其中最大的数即可。
编程中用到set容器: set容器和map容器类似,但是set容器只存储键,map容器是键-值对。比如存20个1,map里存的是<1,20>,set里存的是1。所以不管存多少个同样的数字,set里只有一个数。
因此,set容器适合用来查看某个数是否存在。set.find()返回的是某个数的位置。set.count返回的是某个数的是否存在,存在就return 1 。
代码如下:在986 / 987 这个例子超时了。代码先放在这,就是用的官方题解的Java版改的,看评论里也有好多人超时了。
class Solution {
public:
bool allUnique(string s,int begin,int end)
{
setset1;
for(int i=begin;i
2.滑动窗口:
下面是官方题解:
在暴力法中,我们会反复检查一个子字符串是否含有有重复的字符,但这是没有必要的。如果从索引 i 到 j - 1 之间的子字符串 Sij 已经被检查为没有重复字符。我们只需要检查 s[j]s[j] 对应的字符是否已经存在于子字符串 Sij 中。滑动窗口是数组/字符串问题中常用的抽象概念。
窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即 [i, j)(左闭,右开)。而滑动窗口是可以将两个边界向某一方向“滑动”的窗口。例如,我们将 [i, j)向右滑动 1 个元素,则它将变为 [i+1, j+1)(左闭,右开)。
回到我们的问题,我们使用 HashSet 将字符存储在当前窗口 [i, j)[i,j)(最初 j = i)中。 然后我们向右侧滑动索引 j,如果它不在 HashSet 中,我们会继续滑动 j。直到 s[j] 已经存在于 HashSet 中。此时,我们找到的没有重复字符的最长子字符串将会以索引 i 开头。如果我们对所有的 i 这样做,就可以得到答案。
在实际编程中,发现一个小问题,就是如果用官方题解提供的set模板,向s1中插入字符,会自动按照字符的ASCII码进行排序,这样就打乱了插入的顺序:如测试序列 “ynyo”: n的ASCII码是110 ,y的ASCII码是121,本来想插入的顺序是y,n ,但是insert之后就按照ASCII码大小顺序排序,s1中存储就变成了 n,y。具体的解决办法没想到,就换用了string类型。
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
int i=0,j=0,last=0;
string s1;
//sets1;
while(i
其实感觉这个思想和我之前自己写的那个方法的思想类似,运行时间也是一样的。
3.优化滑动窗口
原来的方法中,i 是一个一个的蹦过去,现在就直接让 i 跳到出现重复字符的位置即可。
当我们知道该字符集比较小的时侯,我们可以用一个整数数组作为直接访问表来替换 Map。
常用的表如下所示:
int [26] 用于字母 ‘a’ - ‘z’或 ‘A’ - ‘Z’
int [128] 用于ASCII码
int [256] 用于扩展ASCII码
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
vectorv(128,0);
int t=0;int ans=0;
for(int i=0;i