Sword はオファー (C++)-JZ48 を指します: 繰り返し文字を含まない最長の部分文字列 (アルゴリズム - 動的プログラミング)

著者: Zhai Tianbao Steven
著作権表示: 著作権は著者に属します。商業的転載の場合は、許可について著者に連絡してください。非商業的な転載の場合は、出典を示してください。

タイトル説明:

文字列から繰り返し文字を含まない最長の部分文字列を見つけて、最長の部分文字列の長さを計算してください。

データ範囲:

 s.length≤40000 s.length≤40000

例:

入力:

「abcabcbb」

戻り値:

3

例証します:

繰り返し文字を含まない最長の部分文字列は「abc」であるため、その長さは 3 です。

問題解決のアイデア:

この質問は動的計画法の古典的な質問です。問題を解決するには 2 つの方法があります。

アイデア 1: 引き違い窓

  1. スライディング ウィンドウを設計し、ウィンドウの右境界を最初に配置し、ハッシュ テーブルを使用して文字の出現数をカウントします。
  2. 繰り返しの文字が表示されると、左の境界線がウィンドウを縮小し始め、繰り返しの文字が消えるまで続きます。
  3. 最も多くの値を更新し続けるだけです。

アイデア 2: 動的プログラミング

  1. ハッシュ テーブルを使用して、最後に出現した文字の添え字を保存します。文字列より 1 大きい長さのベクトルを使用して、i 番目の文字まで維持し続けることができる部分文字列の長さを保存します (v など)。 [0]=0、v[1]=1、v[2]は 1 または 2 です。
  2. トラバースを実行します。現在の文字が繰り返し文字であるかどうかをハッシュ テーブルを使用して判断します。繰り返し文字でない場合は前の部分文字列の長さに 1 を加算し、繰り返し文字がある場合は、その文字と繰り返し文字の間の距離を計算します。は im[s[i] ] ですが、この 2 つの文字の間に他の繰り返し文字がある場合は、そのような状況を考慮する必要があります。繰り返し文字の後の部分文字列には、その文字が以前に出現していないと考えることができます。 is v[i]+1; したがって、v[i]+1 と im[s[i]] の小さい方を比較します。小さい部分文字列は切断されておらず、後で接続し続けることができますが、切断された部分文字列は長さは大きいですが、これ以上増やすことはできません。
  3. 最新の文字添え字と部分文字列の最大長を継続的に更新します。

テストコード:

アイデア 1: 引き違い窓

class Solution {
public:
    // 最长子串
    int lengthOfLongestSubstring(string s) {
        // 定义哈希表
        unordered_map<char, int> m;
        // 滑动窗口遍历
        int result = 0;
        for(int left = 0, right = 0; right < s.length(); ++right){
            // 窗口右边界先行,统计字符出现次数
            m[s[right]]++;
            // 当出现重复字符,窗口左边界右移缩小窗口直到重复字符消失
            while(m[s[right]] > 1){
                m[s[left]]--;
                left++;
            }
            // 持续刷新子串最大长度
            result = max(result, right - left + 1);
        }
        return result;
    }
};

アイデア 2: 動的プログラミング

class Solution {
public:
    // 最长子串
    int lengthOfLongestSubstring(string s) {
        // 定义哈希表,存放的是字符出现的位置下标
        unordered_map<char, int> m;
        int result = 0;
        // v[i]表示截止到i个字符时,能继续维持的子串长度
        // 所以v[0]=0,v[1]=1
        vector<int> v = vector<int>(s.length() + 1, 0);
        // i是字符串中字符下标
        for(int i = 0; i < s.length(); ++i){
            // 当哈希表中没发现重复字符,那就在前面最长子串长度基础上+1
            if(m.find(s[i]) == m.end())
                v[i + 1] = v[i] + 1;
            // 若出现了重复字符,该字符与其重复字符的距离为i-m[s[i]]
            // 但如果两者之间有别的重复字符,那要考虑这类情况
            // 可以认为在其重复字符之后的子串中,该字符未出现过,则有v[i]+1
            // 所以v[i]+1和i-m[s[i]]谁小,取谁,因为小的这个子串没断开
            else
                v[i + 1] = min(v[i] + 1, i - m[s[i]]);
            // 刷新该字符最新下标
            m[s[i]] = i;
            // 刷新最值
            result = max(result, v[i + 1]);
        }
        return result;
    }
};

おすすめ

転載: blog.csdn.net/zhaitianbao/article/details/130686370