谈谈字符串算法

一、判断一个字符串(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;
}

三、最长公共子串
这个问题的核心点在于,如果在两个字符串中找到连续的且公共部分最长的子串
我们可以得到一个递推公式:
d p [ i ] [ j ] { d p [ i − 1 ] [ j − 1 ] + 1 ,   i f   i , j > 0   a n d   x i = y i 0 ,                 i f   i , j > 0   a n d   x i ! = y i 1 ,           i f   i = 0 ∣ ∣ j = 0   a n d   x i = y i   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. 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 
在这里插入图片描述
从上面,我们可以看出,矩阵的每一个对角线上,连线最长的为最长公共子串,最后得出的数字,即为长度。

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

四、最长公共子序列
这个问题和上一个很相似,唯一的不同在于,子序列可以不要求是连续的。因此,我们可以得到如下递推公式:
d p [ i ] [ j ] { 0 ,                     i f   i   o r   j > 0 d p [ i − 1 ] [ j − 1 ] + 1 ,           i f   i , j > 0   a n d   x i = y i m a x ( d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) , i f   i , j > 0   a n d   x i ! = y i   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. 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 
在这里插入图片描述

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

猜你喜欢

转载自blog.csdn.net/weixin_43885417/article/details/103821025