題目
給定字符串,找到它的最長迴文子串,都有哪些思路呢?例如"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:
當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:
// 方法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:
S | # | a | # | b | # | b | # | a | # |
P | 1 | 2 | 1 | 2 | 5 | 2 | 1 | 2 | 1 |
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個:
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表示當前邊界值最大的迴文子串的邊界值。由於超出邊界值的部分無從判斷是否還能與原串組成迴文串,所以要額外判斷。