1. 程式人生 > >最長不含重複字元的子字串

最長不含重複字元的子字串

題目:輸入一個字串,找出字串中最長的不含重複字元的子字串,計算該子字串的長度。假設字串中的字元為“a~z”,例如 arabcacfr ,最長的字串為 rabc 和 acfr ,長度為 4 。————源自《劍指0ffer》

思路:採用暴力的方法,時間複雜度高度 n^3 ,顯然是不可取的。

本題應該採用動態規劃的方法來解決,時間複雜度只需要 n ,以及一個輔助的陣列即可。

我們直接以 arabcacfr 為例子進行分析,從頭開始遍歷字串的字元。

1.第一個字元 a ,此前 a 沒有出現過,所以長度為 f(1) = 1,最長的子字串是  a ; 

2.第二個字元 r ,同樣沒出現過,所以 f(1) = 2 ,最長的子字串是 ar ;

3.第三個字元 a ,因為 在第一個已經出現過了,且 在當前的最長字串 ar 中,所以現在 f(2) = 2,最長子字串更新為 ra;

4.第四個字元 b ,f(3) = 3,第五個字元 c ,f(4) =  4 ,此時最長子字串為  rabc ;

5.第六個字元 a,在我們當前的最長子字串中已經出現,所以 f(5) = 3,最長子字串更新為 bca ;

6.第七個字元c,類似上面,所以 f(6) =  2,最長子字串為 ac ;

7.第八個字元 f,沒出現過 所以 f(7) = 3,最長子字串更新為 acf

8.最後 一個字元 r,前面已經出現過,但是不在我們當前最長子字串acf 中,所以 f(8) = 4,最長子字串更新為acfr 。

分析到此結束,接下來我們如何去實現?這就需要我們用到前面說的輔助陣列 arr,我們用一個初始化的值全為負數的陣列,以字串中的字元temp 為下標進行標記,陣列的值為該字元的位置 i 。當我們遍歷到第 i 個字元,如果

arr[ temp ] 的值為負數,那麼說明這個字元還未出現過,如果 arr[ temp ] 值不為負數,那麼說明這個字元已經出現過,接下來我們要判斷這個字元是否在我們當前統計的最長子字串中。如果不在當前的子字串中,那麼 f( i) = f(i - 1)+ 1;如果在當前的最長子字串中,就像上面的第4 、 5步,我們需要重新計算當前最長子字串,也就是需要找到上一次出現該字元的位置,然後從它後面一位開始統計。具體的程式碼:

package com.hunter.Offer_Example;

import java.util.Scanner;
/**
 * 最長的沒有重複的子字串
 * @author luzi
 *
 */
public class longestNotRepeatSubStr {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner scan = new Scanner(System.in);
		while(scan.hasNext()){
			String str = scan.next();
			System.out.println(getLongestSubStr(str));
		}
	}

	private static int getLongestSubStr(String str) {
		// TODO Auto-generated method stub
		if(str == null)
			return 0;
		
		int[] arr = new int[26];
		int max = 0;
		int count = 0;
		
		for(int i = 0; i < 26; i++){
			arr[i] = -1;
		}
		for(int i = 0; i < str.length(); i++){
			if(arr[str.charAt(i) - 'a'] == -1){
				arr[str.charAt(i) - 'a'] = i;
				count++;
			}
			else
				if(arr[str.charAt(i)] != -1){
					
					//說明當前的字元str.charAt(i)上一次出現的位置在現在統計的子字串前面,,對現在的統計沒影響
					if(i - arr[str.charAt(i) - 'a'] > count){
						count++;
					}
					//當前字元上一次出現的位置在統計之中,我們需要重新統計count即當前的最長子字串
					else{
						count = i - arr[str.charAt(i) - 'a'];
					}
					
					//更新當前字元的位置
					arr[str.charAt(i) - 'a'] = i;
				}
			if(count >= max)
				max = count;
		}
		return max;
	}	
}


因為題目中說明 字元在 a~z 之間,所以我們只需要一個 長度為 26 的陣列,但是如果將字元擴大到所以的ASCII碼,那就需要一個長度為 127 的陣列,並且將 

arr[str.charAt(i) - 'a']
改為
arr[str.charAt(i)]