题目取自leetcode,无重复字符的最长字串。(T3)
题目要求:给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
题目需要字串不重复且连续,从无重复这个关键词很容易联想到用哈希的思想,利用双指针、滑动数组等,时间复杂度也可以接受。
题目已经给出了函数名和传入值:“s数组”。
示例1.输入: s = "abcabcbb"输出: 3原因:最长且不重复字串只有“abc”所以输出3。
示例2.输入: s = "abba"输出: 2原因:最长不重复子串为"ab"。
法一、哈希和滑动数组
ascii码字符在0-255,所以定义一个256的hash数组,来记录该字符是否出现过。
定义q为前指针,p为后指针,后指针每一次都往后移动一格。hash中的值定为该键在数组中的位置加一也就是p+1,方便下一次循环判断q指针是否重复了。
重点:在p指针指到重复字符时还需要判断q指针是否在该字符上次出现位置的右边,还未进入第一个if循环时hash[]的值是该字符上次出现的位置,也就是说,如果q>hash[],那么该字符不算是重复字符,是子串中第一次出现。
若出现重复那么把q提到p相同位置,已经找出一个局部最长字串了。
int lengthOfLongestSubstring(char * s)
{
int count = 0;
int p = 0,q = 0;
int hash[256] = {0};
int len = strlen(s);
while(p<len)
{
if(hash[s[p]]!= 0 && hash[s[p]]>q)
{
q = hash[s[p]];
hash[s[p]] = p+1;
}
else
{
hash[s[p]] = p+1;
}
count = fmax(count,p-q+1);
p++;
}
return count;
}
法二、动态规划
在思考后发现,每一次的最长字符串取值可以从右往左数直到找到重复字符记录下ans,那么从0下标到len下标,求出每个下标的ans,再取所有ans的最大值,就可以找到答案。
再思考后发现,i下标的ans可以由i-1的ans求得,i-1的ans可以由i-2的ans求得......以此类推动态规划的解法也就浮出水面了。
那么应该如何判断每个下标的ans呢?
首先也是定义的数组arr[256],存放每个字符出现的位置,为1-256的字符赋-1的位置初值,这样第一次出现时i - arr[]就是间距了。
重点:取得上一次的ans有两种情况,在代码p1,p2处已经标注,取其最小值就是一个局部ans(谁离你近就要服从谁),再和当前最大的ans相比就取得全局ans。
int lengthOfLongestSubstring(char * s)
{
int len = strlen(s);
if(s == NULL||len == 0)
return 0;
int arr[256];
for(int i = 0;i<256;i++)
arr[i] = -1; //初始化所有的第一次出现的位置为-1
arr[s[0]] = 0; //为第一个字符特意输入位置,为0,因为for循环从1开始会忽
略第一个位置的字符位置
//因此要记录它为0位置
int ans = 1;
int preans = 1;
int p1; //两种情况 1.有重复的字符
int p2; // 2.最远取到上一次的最左处加上自己
for(int i = 1;i<len;i++)
{
p1 = i-arr[s[i]];
p2 = preans+1;
ans = fmax(ans,fmin(p1,p2));
preans = fmin(p1,p2);
arr[s[i]] = i;
}
return ans;
}
题目比较简单,但是这两种解法都比较有意思值得思考,适合像我这样刚刚接触算法的小白。
如有错误或者更妙的解法欢迎评论提出呀!