题目: leetcode 3. Longest Substring Without Repeating Characters.
对定字符串,如何找连续无重复字符的字串?
2021/4/9更新:
三个月后再来看这个…真是一言难尽。
本来很简单的问题为什么当初搞的这么复杂,又复杂又容易出错,要个啥动态规划噢,规划了个寂寞,当时我是怎么蜜汁自信觉得自己理解的很好…
直接用个双指针(sliding window)找最长substring就好了嘛,从0到strlen-1,往左往右找都行,找的过程用hash。
至于C中怎么利用数组搞一个hash,见文末的参考链接中C++解法二或者我新写的博客。
参考链接中就是往左找,为什么往左呢,因为你每遍历一次同时可以记录每个字符最新出现的位置,记录这个就可以找max length。往右的话,每个循环里还得新建一个hash再遍历。
这个用sliding window好理解,left指向的是window左边的index,window位置是left+1~i。所以left一直往右移动,指向的是s[i]字符上一次出现的位置或者不动。left=max(left,myHash[s[i]]); 妙啊!
原标题: 动态规划+unordered_map (C++)
动态回归:
Let s[i] denotes the i th char in the string.
Let s[k: i] denotes the longest substring without repeating characters of s[0:i].
Let OPT(i) denotes the length of the longest substring without repeating characters.
Final result: res=max(res,OPT(i))
说人话:对于第i个字符,当前最大字串为s[k:i] (包括第i个字符),其长度为OPT(i),找出最大的OPT,即为结果。
if s[i] is not in s[k:i-1]:(k是i-1对应的)
OPT(i)=OPT(i-1)+1
else:
OPT(i)=当前字符到左边最近相同字符直接的距离
同时更新k
(题外话:实际上后文写代码中把k定义成了substring左边的index,i.e.substring为k+1到i的字符,是注意事项⚠️里的第三点)
所以,代码实现的关键在于怎么求“当前字符到左边最近相同字符直接的距离”。可以建立一个 HashMap,建立每个字符和其最后出现位置之间的映射。HashMap用unordered_map实现:unordered_map<char, int> myMap;
如此,判断条件也可通过HashMap得知:myMap[s[i]]<k则表示不再substr里。所以更新map应在得到OPT之后。
综上,每一次遍历i时都应该:
- 根据map做如上if,找到k,计算当前OPT(i),同时记录最大的OPT作为res。
- 更新map,记录当前字符出现的位置
#include <iostream>
#include <vector>
#include <stack>
#include<unordered_map>
using namespace std;
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int N=s.size();
if (N==0)
return 0;
int res=0;
int k=-1;//substring is from k+1 to i, including k+1 and i
int opt=0;
unordered_map<char, int> myMap;
for (int i=0;i<N;++i){
if (myMap.count(s[i]) && myMap[s[i]]>k){
//in the substring
k=myMap[s[i]];
opt=i-k;
}
else
opt+=1;
res=max(res,opt);
myMap[s[i]]=i;
}
return res;
}
};
int main()
{
Solution s;
string test1="pwwkew";
cout<<s.lengthOfLongestSubstring(test1)<<endl;
string test2="abcabcbb";
cout<<s.lengthOfLongestSubstring(test2)<<endl;
string test3="a";
cout<<s.lengthOfLongestSubstring(test3)<<endl;
string test4="aa";
cout<<s.lengthOfLongestSubstring(test4)<<endl;
string test5="au";
cout<<s.lengthOfLongestSubstring(test5)<<endl;
return 0;
}
⚠️写码的时候遇到的问题:
- 使用myMap[s[i]]的时候,如果map中没有s[i]会自动添加{s[i],0},而我们的键值也有可能是0,所以会出现问题,解决方法就是加一个判断:myMap.count(s[i]),如果存在会返回1,不存在返回0,并且不会更改myMap。
- 出现“Reference to non-static member function must be called”报错的时候,检查是不是括号写错了!!为这个问题debug好久真的不值得!
- 遇到0/1、</<=的时候要注意,要跟判断里的内容结合。总之就写之前想好吧…