PPTV面試演算法思考-最長對稱子字串
題目
最近在微信公眾號裡看看到了一個PPTV的面試演算法題,感覺難度適中,想試下。題目的內容為求一個字串的最長對稱子字串。如:
輸入 輸出
abba 4
abad 3
acccbaa 3
我的演算法1
自己反覆思索了許多時間。一開始是覺得可以利用對稱字串的一個特點,就是反轉前後兩者是一樣的。所以有如下的演算法:
最長子串長度為max_sub_len
* 1 把輸入的字串a,進行反轉得到b
* 2 把b與a首尾對齊
* 3 找當前相同位置上a與b相同的元素,比如a[0]與b[0], a[1]與b[1],….,a[n]與b[n],找到最長連續相等的長度,記錄此時最長的子串長度長度tmp_max_sub_len。取max_sub_len與tmp_max_sub_len中大者賦給max_sub_len。
* 4 把a左移動一位,如果已經到達a[n]與b[0]對齊時,退出.a[1]與b[0]對齊。重複第3步。
等到演算法完成時,max_sub_len就是最長的子對稱子串長度。
拿hello為例試下:
1,2
a = hello
b = olleh
3 找到a[2]=b[2]=l 最長連續相等為1
4
a = ello
b = olleh
3 找到a[1]=b[0] a[2]=b[1] 最長連線相等為2
4
a = llo
b = elleh
3 找到最長連續相等為1。
4
a = lo
b = elleh
3 找到最長連續相等數為0。
4
a = o
b = elleh
到達終點演算法退出。得到最長對稱子串長度為2。
時間複雜度:比較次數為n + (n-1) + (n-2) + … + 1 = n(n-1)/2 = O(n^2)
空間複雜度:可以通過索引的轉化,實現只使用原陣列。空間複雜度為O(n)
微信評論裡的演算法
有同學在評論裡說,可以把反轉後的a和b放在一起,求它們的最長公共子串。求最長公共子串的演算法屬於動態規劃的演算法,比較複雜,它的實現中,時間複雜度也是O(n^2)。沒有我的演算法1來得直接簡單。具體實現我就不說了。
我的演算法2
第一種演算法算得上不錯了。卻不是最好的。因為它的時間複雜度n^2的係數為0.5,比較大。下面給出一種更好的演算法。
對對稱的情況進行分類。
奇對稱的:aba型別的。
偶對稱的:abba型別的。
第二種演算法是根據兩種型別的不同,以當前字元為中心向兩側輻射,比較對稱兩個字元。從而得到最長的對稱子串。
不再描述其演算法,直接上程式碼(我的github——
#! /usr/bin/python
# -*- coding: utf-8 -*-
'''
最長對稱子字串有兩種形式
奇對稱:abcba
偶對稱:abccba
演算法的時間複雜度為n^2
空間複雜度為n
'''
#寫一個迴圈方便測試
while (True):
max_sym_sub_len = 1
max_single = 1 #奇對稱最長子串
max_double = 0 #偶對稱最長子串
raw_str = raw_input()
# pdb.set_trace()
if raw_str == 'q': #退出
break;
if (len(raw_str) == 1):
pass
elif (len(raw_str) == 2):
if (raw_str[0] == raw_str[1]): #長度為2,兩個字元相等,最長子串為2
max_sym_sub_len = 2
else: #長度>3
start_ind = 1 #從第2個字元開始判斷
tmp_ind = 0
#奇對稱的情形
for start_ind in range(1, len(raw_str) - 1):
max_single_tmp = 1
# 判斷當前的字元距離哪個端點更近
tmp_range = min(start_ind, len(raw_str) - 1 - start_ind)
# print start_ind, tmp_range
for tmp_ind in range(1, tmp_range + 1):
#開始判斷,如果兩側的字元相等,臨時最長的長度加2
if (raw_str[start_ind - tmp_ind] == raw_str[start_ind + tmp_ind]):
max_single_tmp += 2
else:
break;
max_single = max(max_single, max_single_tmp)
for start_ind in range(1, len(raw_str) - 1):
max_double_tmp = 0
# 判斷當前的字元距離第一個字元,和當前字元的下個字元距離
# 最後一個端點的距離中哪個更近
tmp_range = min(start_ind, len(raw_str) - 2 - start_ind)
# print start_ind, tmp_range
# tmp_ind 應該從0開始
for tmp_ind in range(tmp_range + 1):
#開始判斷,如果兩側的字元相等,臨時最長的長度加2
if (raw_str[start_ind - tmp_ind] ==
raw_str[start_ind + 1 + tmp_ind]):
max_double_tmp += 2
else:
break;
max_double= max(max_double, max_double_tmp)
max_sym_sub_len = max(max_single, max_double)
print max_sym_sub_len