算法 -- 四種方法獲取的最長“迴文串”,並對時間複雜進行分析對比&PHP

迴文串

迴文串”是一個正讀和反讀都一樣的字符串,比如“level”或者“noon”等等就是迴文串。 -- 來自百度百科

關於獲取字符串中最長的迴文串的算法中,目前有很多算法,本文中主要是用PHP來實現的算法之一。


算法一:暴力解法

暴力計算出所有的字符串並判斷。時間複雜度:O(n^3)

<?php
//1.  判斷字符串是否是迴文字符串
function isPalindrome($str)
{
    if ($str === strrev($str)) {
        return 1;
    } else {
        return 0;
    }
}
//2. 獲取一個字符串有多少子串,設置了子串長度最小爲2
function getAllSubstring($str)
{
    $all_str_arr = [];
    for ($i = 0; $i < strlen($str); $i++) {
        for ($j = 0; $j < strlen($str) - $i + 1; $j++) {
            if (strlen(substr($str, $i, $j)) > 1) {
                $all_str_arr[] = substr($str, $i, $j);
            }
        }
    }
    return $all_str_arr;
}
//3. 獲取一維數組中,元素長度最長的一組
function getArrMaxStr($arr)
{
    $max = 0;
    foreach ($arr as $k => $v) {
        if (strlen($arr[$max]) < strlen($v)) {
            $max = $k;
        }
    }
    return $arr[$max];
}
//4. 取得字符串中,最長的迴文串
function getMaxPalindrome1($str)
{
    if (isPalindrome($str)) {
        return $str;
    } else {
        // 拆分成多個字符串,然後循環比較
        foreach (getAllSubstring($str) as $k => $v) {
            if (isPalindrome($v)) {
                $max_str[] = $v;
            }
        }
        return getArrMaxStr($max_str);
    }
}

以上四個步驟就是獲取字符串中最長的迴文串,但是這個算法只適合較短的字符串,由上大家也可以看出,步驟2中求每一個子串時間複雜度O(N^2),步驟4中,再循環判斷子串是不是迴文串O(N),兩者是相乘關係,所以時間複雜度爲O(N^3)。

暴力求解的優化:

每次判斷一個字符串是否是迴文字符串時,將每次的判斷結果存存起來,之後再用就不用重新計算了。但需要從後向前遍歷,這樣纔會用得到提前存儲的結果。時間複雜度爲O(n^2)。(這裏就不代碼說明了)

算法二:移動中心法

先假設某個位置爲迴文字符串的中心,然後查詢以此位中心的最長迴文字符串。遍歷中心,即可找到全局最長子串。時間複雜度爲O(n^2)。

function getMaxPalindrome2($str)
{
    if (is($str)) {
        return $str;
    } else {
        $len = strlen($str);
        // 假如得到的迴文串 爲 偶數,abba,即中點是空隙
        $max_str = '';
        $max_arr = [];
        for ($i = 0; $i < $len; $i++) {
            $left = $i;
            $right = $i + 1;
            while ($right < $len && $left >= 0 && $str[$left] == $str[$right]) {
                if (strlen($max_str) <= $right - $left + 1) {
                    $max_str = substr($str, $left, $right - $left + 1);
                    if (is($max_str)) {
                        $max_arr[] = $max_str;
                    }
                }
                $left--;
                $right++;
            }
        }
        // 假如得到的迴文串 爲 奇數,aba,即中點是字符
        for ($i = 0; $i < $len; $i++) {
            $left = $i - 1;
            $right = $i + 1;
            while ($right < $len && $left >= 0 && $str[$left] == $str[$right]) {
                if (strlen($max_str) <= $right + 1 - $left) {
                    $max_str = substr($str, $left, $right - $left + 1);
                    if (is($max_str)) {
                        $max_arr[] = $max_str;
                    }
                }
                $left--;
                $right++;
            }
        }
        // 兩個合成一個的話,就是,只有當奇數迴文串的大於或等於偶數迴文串的時候,
        $longest_str = [];
        foreach ($max_arr as $v) {
            if (strlen($max_str) == strlen($v)) {
                $longest_str[] = $v;
            }
        }
        return $longest_str;
    }
}
// 判斷字符串是否是迴文字符串
function isPalindrome($str)
{
    if ($str === strrev($str)) {
        return 1;
    } else {
        return 0;
    }
}

算法三:公共字符串法

​​利用公共最長字符串,時間複雜度:O(n^2)。

ps:方法getLongestSameStr(),在我的這篇文章裏:算法 -- 求最長公共字符串&PHP

function getMaxPalindrome3($str)
{
    //1. 判斷是不是迴文
    if (is($str)) return $str;
    //2. 利用最長公共字符串的方法求
    $arr = getLongestSameStr($str, strrev($str));
    return $arr;
}
// 判斷字符串是否是迴文字符串
function isPalindrome($str)
{
    if ($str === strrev($str)) {
        return 1;
    } else {
        return 0;
    }
}

算法四:Manacher算法

經典的Manacher 算法,優勢在於避免了算法②奇偶數討論的問題,簡化了算法②邊界判斷,還記錄了當前字符串的“迴文狀態”,利用之前的迴文狀態來求當前迴文狀態 ,體現了算法③動態規劃的思想,存儲數據,不用再次計算。時間複雜度爲O(n)。

function getMaxPalindrome4($str)
{
    // 初始化最大回文序列中間座標
    $maxxy = 0;
    // 初始化最大回文長度
    $maxLength = 0;
    // 初始化一個空數組存儲每次的迴文序列中間座標(key)和迴文長度(value)
    $arr = [];
    // 通過在每個字符的兩邊都插入一個特殊的符號,將所有的迴文子串都轉換成奇數長度;
    // 在字符串的開始和結尾加入另一個特殊字符,這樣就不用特殊處理越界問題
    $newStr = "^#" . implode("#", str_split($str)) . "#\0";
    // 遞推,每次取一個數作爲中間座標
    for ($i = 2; $newStr[$i] != "\0"; $i++) {
        // 每個中間座標的初始迴文長度爲1
        $arr[$i] = 1;
        // 根據每個中間座標往兩頭匹配是否相等
        while ($newStr[$i - $arr[$i]] == $newStr[$i + $arr[$i]]) {
            // 每匹配成功一次,則當前座標的最大回文長度加一
            $arr[$i]++;
        }
        // 判斷當前迴文長度是否大於最大的迴文長度,大於則進去if代碼塊更新最大回文次數和更新最大回文中間座標
        if ($arr[$i] > $maxLength) {
            $maxLength = $arr[$i];//字符串的長度
            $maxxy = $i;//字符串的末位置座標

        }
    }
    // 截取最大回文長度的字符串

    $res = substr($newStr, $maxxy - $maxLength + 1, $maxLength * 2 - 1);
    // 清除開始加入的字符並返回
    return str_replace('#', "", $res);
}

總結:由上可以清晰看出,時間複雜度④>③=②>①,算法四是最優方案。

 

歡迎補充!.

QEの大獅子!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章