最長迴文的五種實現(js代碼實現)

題目

給定字符串,找到它的最長迴文子串,都有哪些思路呢?例如"adaiziguizhongrenenrgnohziugiziadb",迴文字串很多了,但最長的是"daiziguizhongrenenrgnohziugiziad"。

解題思路

思路1:

暴力法,外面的兩層循環找到所有子串,第三層循環判斷子串是否是迴文。方法的時間複雜度爲O(n^3),空間複雜度爲O(1)。

代碼實現:

function makeOdd(str){
	var len = str.length;
	if(len % 2 === 1){
		return str;
	}
	var newStr = '#';
	for(i = 0;i<len;i++){
		newStr += str[i]+'#';
	}
	return newStr;
}
function judge(str){
	(str.length%2 === 0) && (str = makeOdd(str));
	var len = str.length,
		half = Math.floor(len/2),
		last = len-half;
	var i = 0;
	while(i<=last){
		if(str[half-i] !== str[half+i]){
			return false;
		}
		i++;
	}
	return true;
}
function getAllSubs(str){
	var len = str.length,
		res = [];
	for(var i = 0;i<len;i++){
		for(var j = i;j<len;j++){
			var sub = str.substring(i,j+1);
			console.error(sub);
			if(sub && judge(sub)){
				res[res.length] = sub;
			}
		}
	}
	return res;
}
console.log(getAllSubs('abaac'));

思路2:

動態規劃的方法。開闢一個P[i][j]用來表示str[i..j]是否爲迴文,P[i][j]的狀態轉移方程如下:


當i==j時,P[i][j]=true
當i+1==j時,P[i][j]=str[i]==str[j]
其他,P[i][j]=P[i+1][j-1]&&(str[i]==str[j])


這樣,這個方法的時間複雜度爲O(n^2),空間複雜度爲O(n^2)。比暴力法有很大的改進。
代碼實現:
// 獲取所有的子串迴文情況
	function getP2(str){
		var len = str.length,
			gap = '_';
		var p = {};
		var i,j,L;
		// 只有一個字符的情況是迴文
		for( i =0;i<len;i++){
			p[i+gap+i] = true;
		}
		// L是i和j之間的間隔數(因爲間隔數從小到大漸增,所以大的間隔數總能包含小的間隔數)
		for(L=2;L<=len;L++){
			// 從0開始
			for(i=0;i<=len-L;i++){
				j = i+L-1;
				if(L === 2){
					p[i+gap+j] = (str[i] === str[j]);
				}else{
					p[i+gap+j] = (str[i]===str[j])&&p[i+1+gap+(j-1)];
				}
			}
		}
		return p;
	}

思路3:

可以從上面那個方法的狀態轉移方程獲得啓發,對於每一個迴文子串可以先確定一箇中心,然後向兩邊擴展,這樣可以在時間複雜度O(n^2),空間複雜度O(1)的情況下完成,需要注意的是,長度爲奇數和偶數的中心的情況是不同的。
代碼實現:
// 方法3
function getP3(str){
	var maxLength = 1,
		start = 0,
		len = str.length,
		low,high;
	for(var i=1;i<len;++i){
		low = i-1;
		high = i;
		while(low>=0 && high < len && str[low] === str[high]){
			if(high-low+1>maxLength){
				start = low;
				maxLength = high-low+1;
			}
			--low;
			++high;
		}
	}
	low = i-1;
	high = i+1;
	while(low>=0 && high < len && str[low] === str[high]){
		if(high-low+1>maxLength){
			start = low;
			maxLength = high-low+1;
		}
		--low;
		++high;
	}
	return maxLength;
}

思路4:

第四個方法採用後綴數組,將最長迴文子串的問題轉化爲最長公共前綴的問題。具體的做法就是:將整個字符串翻轉之後,拼接到原字符串後,注意用特殊字符分開,這樣問題就變成了新的字符串的某兩個後綴的最長公共前綴的問題了。這個方法比較強大,很多字符串的問題都能夠巧妙的解決。不過實現起來也相對比較難,好的實現和差的實現時間複雜度相差很大。

後綴數組是一種對字符串處理的比較有力的實現方案,可以用於求解最大公共子串、最長迴文串等問題。但是其實現思路還是比較難以理解的。基本的思想就是獲取字符串的後綴字符串,然後獲得後綴字符串的最長公共前綴。其中牽扯到一系列的表示(如後綴數組、排序數組等),對於如何求得這些表示又有不同的對應方案。具體的論文可參考:後綴數組——處理字符串的有力工具

思路5:

第五個方法叫做Manacher算法,是一種線性時間的方法,非常巧妙。引入一個技巧,可以使得奇數和偶數的情況統一處理。具體做法如下:abba轉換爲#a#b#b#a#,也就是在每一個字符兩邊都加上一個特殊字符,這樣,不管是奇數長度的字符串還是偶數長度的字符串就都能轉化爲奇數長度的字符串了。
然後創建一個新的P[i]表示,以第i個字符爲中心的迴文字串的半徑。例如上面的例子,對應的P如下,設S爲原始字符串:

S # a # b # b # a #
P 1 2 1 2 5 2 1 2 1
通過觀察上面的表,大家可以發現P[i]-1就是實際迴文字串的長度。如果知道P,遍歷一次就知道最長的迴文子串。可以該如何計算P呢?這是這個算法最核心的部分。
代碼實現:
function getP5(str){
	var p = [], 
		mx = 0,
		id = 0;
	var i;
	// 將字符串轉化爲奇數長度獲取到新的字符串
	var newStr = '#';
	var len = str.length;
	for(i = 0;i<len;i++){
		newStr += str[i]+'#';
	}
	var newLen = newStr.length;
	for(i = 0;i<newLen;i++){
		p[i] = 0;
	}
	// 時間複雜度爲O(n),空間複雜度爲O(1)獲取到所有的子迴文的長度值組成的數組
	for (i = 0;i < newLen; i++) {
		// mx表示當前邊界值最大的迴文子串的邊界值
		p[i] = mx > i ? Math.min(p[2*id-i], mx-i) : 1;
		// 超出其半徑的位置再做額外判斷
		while ((newStr[i + p[i]] == newStr[i - p[i]]) && newStr[i + p[i]]){
			p[i]++;
		}
		// 獲取到邊界最大的迴文子串的中心位置以及邊界值,以保證後續迭代可以做以上快捷處理
		if (i + p[i] > mx) {
			id = i;
			mx = id + p[i];
		}
	}
	return p;
}

通過代碼實現後,發現上面的博客對於其中的原理的分析並不十分準確。最基本的原則就是2個:
1、如果一個字符串(統一轉化爲了奇數長度)是迴文串,中間字符位置爲m。如果其子串S[i,i+x]是迴文串,那麼其相對於中間字符的對稱子串S[2m-i,2m-(i+x)]也一定是迴文串;
2、迴文串一定是基於中心字符串對稱的。也就是:S[i] == S[2m-i]。
然後分析下核心代碼:
p[i] = mx > i ? Math.min(p[2*id-i], mx-i) : 1;
		// 超出其半徑的位置再做額外判斷
		while ((newStr[i + p[i]] == newStr[i - p[i]]) && newStr[i + p[i]]){
			p[i]++;
		}
		// 獲取到邊界最大的迴文子串的中心位置以及邊界值,以保證後續迭代可以做以上快捷處理
		if (i + p[i] > mx) {
			id = i;
			mx = id + p[i];
		}

分析後得知,mx並不是像博文中說的表示最大回文串的邊界值,mx表示當前邊界值最大的迴文子串的邊界值。由於超出邊界值的部分無從判斷是否還能與原串組成迴文串,所以要額外判斷。

注:以上的思路分析都是基於待字閨中的面試題分析,代碼實現爲自己的實現。

發佈了45 篇原創文章 · 獲贊 10 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章