一、判斷一個字符串(strOne)是否是另一個字符串(strTwo)的子串
思路:
- 從下標0開始遍歷strTwo,一直遍歷到strlen(strTwo) -strlen(strOne),
- 內嵌循環,依次和 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;
}
三、最長公共子串
這個問題的核心點在於,如果在兩個字符串中找到連續的且公共部分最長的子串。
我們可以得到一個遞推公式:
從上面,我們可以看出,矩陣的每一個對角線上,連線最長的爲最長公共子串,最後得出的數字,即爲長度。
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
四、最長公共子序列
這個問題和上一個很相似,唯一的不同在於,子序列可以不要求是連續的。因此,我們可以得到如下遞推公式:
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