昨天被網易二面投屏算法虐了一番,痛定思痛,每日一道算法題。下面來看看這道算法題:
給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度和字符。
輸入: "abcabcbb"
輸出: 3,abc,bca,cab
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
這道題其實是一道經典的滑動窗口應用的題目,但是上網查了一下,結果很多的OC算法代碼其實都是錯誤的,例如點擊最多的這位(【iOS每日算法】無重復字符的最長子串(滑動窗口):給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度。 - 簡書),下面先上代碼:
`- (NSInteger)checkTheStringScan:(NSString *)content {
? ? //abdfhabcojdsnagjsu
? ? NSInteger length = content.length;
? ? // 記錄已經遍歷過的字符所在位置的字典
? ? NSMutableDictionary *checkedDict = [[NSMutableDictionary alloc]init];
? ? //設置我們需要得到的最長子串的長度
? ? NSInteger maxLength = 0;
? ? //記錄子串起始位置的指針
? ? NSInteger start = 0;
? ? // 循環字符串 i 為右邊指針,不斷向右移動,像一個往右劃的窗口
? ? for (NSInteger i = 0; i < length ; i++) {
? ? ? ? NSRange letterRange = NSMakeRange(i, 1);
? ? ? ? NSString *letter = [content substringWithRange:letterRange];
? ? ? ? // 判斷是否含有滑動加入的字符
? ? ? ? if([[checkedDict allKeys]containsObject:letter]) {
? ? ? ? ? ? // 如果有將記錄子串起始位置的start移動到該字符之前出現的位置的下一個位置
? ? ? ? ? ? // 相當于把左邊指針向右拉,直到不包含當前遍歷的字符,這樣這個子串中就沒有重復字符了
? ? ? ? ? ? start = [[checkedDict objectForKey:letter] integerValue] + 1;
? ? ? ? }
? ? ? ? //注意這里必須也是網上常常錯誤的地方,每個i即右指針都需要經過計算得到當前子串長度
? ? ? ? NSInteger tempLength = i - start + 1;
? ? ? ? // 取最長的子串
? ? ? ? if(tempLength >=maxLength) {
? ? ? ? ? ? maxLength = tempLength;
? ? ? ? ? ? NSLog(@"%@",[content substringWithRange:NSMakeRange(start, maxLength)]);
? ? ? ? }
? ? ? ? // 更新子串中該字符位置
? ? ? ? [checkedDict setObject:@(i) forKey:letter];
? ? }
? ? return maxLength;
}`
實現思路的話,主要就是通過for循環,以i為右指針開始滑動,當遇到相同字符的時候,對兩者后面的一個進行位置i的記錄(這里使用了字典的key來實現的,也可以使用Set實現,唯一性保證),記錄完相同字符后,左指針start記錄相同字符前者的后一個字符位置,取左右指針的中間長度tempLength計算方式:i-start+1,記錄完成后遍歷自校tempLength的最大長度即最長不重復子串的長度,start即最長子串起始位置,得出最長子串內容如下:
`2021-07-15 15:40:19.677857+0800 arithmeticTest[19183:2180250] fhabcojdsn
2021-07-15 15:40:19.678016+0800 arithmeticTest[19183:2180250] 10`
第二種更簡單的NSMutableSet實現的滑動窗口,直接代碼:
`- (void)checkTheStringScanTwo {
? ? NSString *str = @"abdfhabcojdsnagjsu";
? ? NSMutableSet *set = [NSMutableSet new];
? ? long n = str.length;
? ? //ans:最長子串長度,j:右指針,i:左指針
? ? int ans = 0, i = 0, j = 0 ,index = 0;
? ? while (i<n && j<n) {
? ? ? ? //和上一個算法相反,判斷不包含則添加更合理
? ? ? ? if (![set containsObject:[str substringWithRange:NSMakeRange(j,1)]]) {
? ? ? ? ? ? //右指針滑動
? ? ? ? ? ? [set addObject:[str substringWithRange:NSMakeRange(j++, 1)]];
? ? ? ? ? ? //取指針區間內最大值
? ? ? ? ? ? if (j - i > ans) {
? ? ? ? ? ? ? ? ans = j - i;
? ? ? ? ? ? ? ? index = i;
? ? ? ? ? ? }
? ? ? ? }else{
? ? ? ? ? ? //出現相同字符則刪除前一個,并i++,左指針右偏移一
? ? ? ? ? ? [set removeObject:[str substringWithRange:NSMakeRange(i++, 1)]];
? ? ? ? }
}
? ? NSLog(@"%@",[str substringWithRange:NSMakeRange(index, ans)]);
}`
平時多積累,面試別緊張,冷靜思考,其實都不難。