1. 程式人生 > >PPTV面試演算法思考-最長對稱子字串

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——

algorithm_ds)。這種演算法在最壞的情況下,是aaaaaaaaaaaa,全是一個字元的字串。要比較n(n-1)次。正常隨機輸入的情況下,o(n^2)的係數是很小的。

#! /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