談談字符串算法

一、判斷一個字符串(strOne)是否是另一個字符串(strTwo)的子串
思路:

  1. 從下標0開始遍歷strTwo,一直遍歷到strlen(strTwo) -strlen(strOne),
  2. 內嵌循環,依次和 strOne每一個字符是否相等,如果不等,strTwo右移,進行下一輪循環。如果相等返回 true。
function isChildStr($strOne, $strTwo) {
    $lengthOne = strlen($strOne);
    $lengthTwo = strlen($strTwo);
    $j = 0;
    for ($i = 0; $i <= $lengthTwo - $lengthOne; $i++) {
        for ($j = 0; $j < $lengthOne; $j++) {
            if ($strTwo[$i + $j] !== $strOne[$j]) break;
        }
        if ($j === strlen($strOne)) return true;
    }
    return false;
}

測試數據:
sss  sdshss   
輸出  false
sss  sdshsss
輸出  true

二、一個字符串中最長不重複子串問題
思路:
4. 記錄當前字符之前的最長不重複子串長度,記爲L(i - 1),其中i爲當前字符的位置。
5. 遍歷字符串:
(1)若當前字符第一次出現,則MaxL = L(i - 1) + 1
(2)若不是第一次出現,計算當前字符與它上次出現位置之間的距離,記爲d:
  1> 若d > L(i - 1),說明前一個非重複子字符串中沒有包含當前字符,則MaxL = L(i - 1) + 1。
  2> 若d < L(i - 1),說明前一個非重複子字符串中已經包含當前字符,則MaxL = d

function findMaxStringLength($str){
    if ($str == null || $str === "") return '不合法的字符串';
    $maxLength = 0;                                 //最大長度
    $currentLength = 0;                             //當前字符前面的不重複子串長度
    $position = array();
    for ($i = 0; $i < 26; $i++) {
        $position[$i] = -1;                         //初始化爲-1,負數表示沒出現過
    }
    for ($i = 0; $i < strlen($str); $i++) {
        $char = ord($str[$i]) - ord('a');
        $prePosition = $position[$char];
        //計算當前字符與它上次出現位置之間的距離
        $distance = $i - $prePosition;
        //當前字符第一次出現,或者前一個非重複子字符串中沒有包含當前字符
        if ($prePosition < 0 || $distance > $currentLength) {
            $currentLength++;
        } else {
            //如果當前長度大於最大長度,更新
            if ($currentLength > $maxLength) {
                $maxLength = $currentLength;
            }
            $currentLength = $distance;
        }
        //更新字符出現的位置
        $position[$char] = $i;
    }
    if ($currentLength > $maxLength) {
        $maxLength = $currentLength;
    }
    return $maxLength;
}

三、最長公共子串
這個問題的核心點在於,如果在兩個字符串中找到連續的且公共部分最長的子串
我們可以得到一個遞推公式:
dp[i][j]{dp[i1][j1]+1, if i,j>0 and xi=yi0,        if i,j>0 and xi!=yi1,     if i=0j=0 and xi=yi dp[i][j]\left\{ \begin{array}{c} dp[i - 1][j - 1] + 1, if  i, j >0 and  xi = yi\\ 0,        if  i, j >0 and  xi != yi\\ 1,      if  i = 0 || j=0 and  xi = yi \end{array}\right.
在這裏插入圖片描述
從上面,我們可以看出,矩陣的每一個對角線上,連線最長的爲最長公共子串,最後得出的數字,即爲長度。

function getMaxLengthStr($strOne, $strTwo)
{
    $dp = array(array());
    $length = 0;
    $maxEnd = 0;
    for ($i = 0; $i < strlen($strOne); $i++) {
        for ($j = 0; $j < strlen($strTwo); $j++) {
            //如果相等
            if ($strOne[$i] == $strTwo[$j]) {
                $dp[$i][$j] = ($i > 0 && $j > 0) ? $dp[$i - 1][$j - 1] + 1 : 1;
                if ($dp[$i][$j] > $length) {
                    $length = $dp[$i][$j];
                    $maxEnd = $j;
                }
            } else {
                $dp[$i][$j] = 0;
            }
        }
    }
    echo mb_substr($strTwo, $maxEnd - $length + 1, $length);
}

測試用例:
123ABCD4567 ABE12345D6
輸出:
123

四、最長公共子序列
這個問題和上一個很相似,唯一的不同在於,子序列可以不要求是連續的。因此,我們可以得到如下遞推公式:
dp[i][j]{0,          if i or j>0dp[i1][j1]+1,     if i,j>0 and xi=yimax(dp[i][j1],dp[i1][j]),if i,j>0 and xi!=yi dp[i][j]\left\{ \begin{array}{c} 0,          if  i or  j >0\\ dp[i - 1][j - 1] + 1,     if  i, j >0 and  xi = yi\\ max(dp[i][j - 1], dp[i - 1][j]), if  i, j >0 and  xi != yi \end{array}\right.
在這裏插入圖片描述

function findMaxLcs($strOne, $strTwo) {
    $arrOne = array_fill(0, strlen($strOne) + 1, array_fill(0, strlen($strTwo) + 1, 0));
    $arrTwo = LcsLength($strOne, $strTwo, $arrOne);
    echo $arrTwo[strlen($strOne)][strlen($strTwo)] . "\n";
    Lcs(strlen($strOne), strlen($strTwo), $strOne, $arrOne);
}

/**
 * $arrTwo[i][j]存儲Xi和Yj的最長公共子序列的長度
 * $arrOne[i][j]記錄$arrTwo[i][j]的值是由哪一個子問題的解得到的,在構造最長公共子序列時要用到
 * @param $strOne
 * @param $strTwo
 * @param $arrOne
 * @return array
 */
function LcsLength($strOne, $strTwo, &$arrOne) {
    $arrTwo = array_fill(0, strlen($strOne) + 1, array_fill(0, strlen($strTwo) + 1, 0));
    for ($i = 1; $i <= strlen($strOne); $i++) {
        for ($j = 1; $j <= strlen($strTwo); $j++) {
            if ($strOne[$i - 1] == $strTwo[$j - 1]) {
                $arrTwo[$i][$j] = $arrTwo[$i - 1][$j - 1] + 1;
                $arrOne[$i][$j] = 1;
            } else {
                if ($arrTwo[$i - 1][$j] >= $arrTwo[$i][$j - 1]) {
                    $arrTwo[$i][$j] = $arrTwo[$i - 1][$j];
                    $arrOne[$i][$j] = 2;
                } else {
                    $arrTwo[$i][$j] = $arrTwo[$i][$j - 1];
                    $arrOne[$i][$j] = 3;
                }
            }
        }
    }
    return $arrTwo;
}
function Lcs($i, $j, $strOne, $arrOne) {
    if ($i == 0 || $j == 0) return ;
    //判斷$arrOne進入不同的分支
    if ($arrOne[$i][$j] == 1) {
        Lcs($i - 1, $j - 1, $strOne, $arrOne);
        echo $strOne[$i - 1];
    } else {
        ($arrOne[$i][$j] == 2) ? Lcs($i - 1, $j, $strOne, $arrOne) : Lcs($i, $j - 1, $strOne, $arrOne);
    }
}

測試用例:
ABCBDA, BDCABA
輸出:
4
BCBA

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