最長子串

Rauze發表於2024-04-08

📃題目

給定一個字串,找出包含最多2個不同字元的最長子串長度

示例 1:

輸入:axyxxxxyyyxdddadayx
輸出:10
解釋:其中符合條件的最長子串是 xyxxxxyyyx,子串中只包含x和y兩個不同字元

🤔思路

子串問題第一反應想到了動態規劃,開始動規五部曲分析

1、確定dp陣列以及下標含義

dp陣列應該是幾維呢?

設定一維的話,dp[i]中應該存放的是以下標i結尾符合要求的字串長度,但是在判斷字串是否符合要求時,我們還需要額外的資訊——該字串中不同字元的個數

因此dp陣列設定成為二維,即dp[i][j],i與一維的定義相同,而j表示以下標i結尾,有j+1個不同字元。

2、確定遞推公式

在遞推過程中存在兩種情況:

當前字元與前一個字元相等,即s[i]==s[i-1]

dp[i][0] = dp[i-1][0] + 1;
// 因為dp[i-1][1]>dp[i-1][0],所以不用考慮dp[i-1][0]+1的情況
dp[i][1] = dp[i-1][1] + 1;

當前字元與前一個字元不相等,即s[i]≠s[i-1]

//只能有一個不同字元,那麼只能是s[i]這一個了
dp[i][0] = 1;
dp[i][1] = dp[i-1][0] + 1;

dp[i][1]乍一看感覺沒問題,但是執行的時候,它忽略了一種場景,即以i-1結尾有兩個不同字元的字串,其中一個字元與s[i]相同,那麼這種情況應該是dp[i][1] = dp[i-1][1] + 1

那麼如何去判斷另外一個”不知道位置“的字元是否與s[i]相同呢

其實這個位置與dp[i-1][1]息息相關,可以透過i-dp[i-1][1]-1獲得另一個字元的下標位置,因為[i-dp[i-1][1],i-1]這個區間內的都是相同字元

所以第二種情況的遞推公式,程式碼如下:

//只能有一個不同字元,那麼只能是s[i]這一個了
dp[i][0] = 1;
if (s[i] != s[i - dp[i - 1][0] - 1]) dp[i][1] = dp[i - 1][0] + 1;
else dp[i][1] = dp[i - 1][1] + 1;

3、dp陣列如何初始化

陣列需要初始化的是dp[0][0]和dp[0][1],兩個分別表示下標i字元結尾有1個不同字元和有2個不同字元的字串長度,從實際結果上看長度都是1

4、確定遍歷順序

從遞推公式上可以看出,dp[i]需要dp[i-1]計算,所以應該從上至下計算

5、舉例推導公式

使用字串前半部分axyxxxxy為例

dp[i][j] 0 1
0 1 1
1 1 2
2 1 2
3 1 3
4 2 4
5 3 5
6 4 6
7 1 7

最終遍歷dp陣列尋找最大值得到結果

程式碼如下:

#include <bits/stdc++.h>
using namespace std;

int main() {
 string s;
 cin >> s;
 vector<vector<int>> dp(s.size(), vector<int>(2, 0));
 dp[0][0] = 1;
 dp[0][1] = 1;
 for (int i = 1; i < s.size(); i++) {
  if (s[i] == s[i - 1]) {
   dp[i][0] = dp[i - 1][0] + 1;
   dp[i][1] = dp[i - 1][1] + 1;
  } else {
   dp[i][0] = 1;
   if (s[i] != s[i - dp[i - 1][0] - 1]) dp[i][1] = dp[i - 1][0] + 1;
   else dp[i][1] = dp[i - 1][1] + 1;
  }
  cout << dp[i][1] << endl;
 }
 int result = dp[0][1];
 for (int i = 1; i < s.size(); i++) {
  result = max(result, dp[i][1]);
 }
 cout << result;
 return 0;
}

🔚總結

本題屬於動態規劃型別的題目,只是在遞推公式的細節上需要琢磨,透過dp[i-1][0]處理不同字元是否重合是題目的關鍵,否則會卡測試案例

雖然是基礎演算法的稍微改進,但是自己構想寫出來並提交AC的時候,那種成就感讓人還想再來一題

相關文章