代碼題(56)— 最長重復子串、無重復字符的最長子串


1、最長的重復子串

  尋找一個字符串中最長的重復子串

  最大后綴方法思路:

1. 用字符串指針數組保存用戶輸入的字符串的所有后綴字符串

2. 將后綴字符串集合進行排序

3. 比較相鄰字符串的公共子串長度,找到長度最大值,保存相應字符串即為所求

  空間復雜度:求長度為n的字符串的后綴,需要O(n)的空間復雜度 
  時間復雜度:產生后綴數組-時間復雜度O(N)、對后綴數組排序是O(N*NlogN),第一個N表示字符串的比較,后面NlogN使用快排排序。依次檢測相鄰兩個后綴的公共長度-時間復雜度O(N*N)、取出最大公共長度的前綴-時間復雜度O(N)。直接用sort排序,時間復雜度是nlog(n)。

總的時間復雜度是O(N*NlogN)

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include <vector>
#include<string>
#include<sstream>
#include<map>
#include<set>
#include <functional> // std::greater
using namespace std;

int getCommon(string s1, string s2)
{
    int i = 0;
    for (; i < s1.size()&& i<s2.size(); ++i)
    {
        if (s1[i] != s2[i])
            break;
    }
    return i;
}

int main()
{
    string str;
    cin >> str;
    vector<string> strs;
    for (int i = 0; i < str.size(); ++i)
    {
        strs.push_back(str.substr(i));
    }
    sort(strs.begin(),strs.end());
    int maxlen = 0;
    string res;
    for (int i = 1; i < strs.size(); ++i)
    {
        int len = getCommon(strs[i-1], strs[i]);
        if (maxlen < len)
        {
            maxlen = max(maxlen, len);
            res = strs[i - 1].substr(0,len);
        }
    }
    cout << res<<"\n" << maxlen << endl;

    return 0;
}

2、3. 無重復字符的最長子串

給定一個字符串,找出不含有重復字符的最長子串的長度。

示例 1:

輸入: "abcabcbb"
輸出: 3 
解釋: 無重復字符的最長子串是 "abc",其長度為 3。

示例 2:

輸入: "bbbbb"
輸出: 1
解釋: 無重復字符的最長子串是 "b",其長度為 1。

示例 3:

輸入: "pwwkew"
輸出: 3
解釋: 無重復字符的最長子串是 "wke",其長度為 3。
     請注意,答案必須是一個子串,"pwke" 是一個子序列 而不是子串。

 方法一、

   我們之前手動推導的方法實際上是維護了一個滑動窗口,窗口內的都是沒有重復的字符,我們需要盡可能的擴大窗口的大小。由於窗口在不停向右滑動,所以我們只關心每個字符最后出現的位置,並建立映射。窗口的右邊界就是當前遍歷到的字符的位置,為了求出窗口的大小,我們需要一個變量left來指向滑動窗口的左邊界,這樣,如果當前遍歷到的字符從未出現過,那么直接擴大右邊界,如果之前出現過,那么就分兩種情況,在或不在滑動窗口內,如果不在滑動窗口內,那么就沒事,當前字符可以加進來,如果在的話,就需要先在滑動窗口內去掉這個已經出現過的字符了,去掉的方法並不需要將左邊界left一位一位向右遍歷查找,由於我們的HashMap已經保存了該重復字符最后出現的位置,所以直接移動left指針就可以了。我們維護一個結果res,每次用出現過的窗口大小來更新結果res,就可以得到最終結果啦。

  這里我們可以建立一個256位大小的整型數組來代替HashMap,這樣做的原因是ASCII表共能表示256個字符,所以可以記錄所有字符,然后我們需要定義兩個變量res和left,其中res用來記錄最長無重復子串的長度,left指向該無重復子串左邊的起始位置,然后我們遍歷整個字符串,對於每一個遍歷到的字符,如果哈希表中該字符串對應的值為0,說明沒有遇到過該字符,則此時計算最長無重復子串,i - left +1,其中i是最長無重復子串最右邊的位置,left是最左邊的位置,還有一種情況也需要計算最長無重復子串,就是當哈希表中的值小於left,這是由於此時出現過重復的字符,left的位置更新了,如果又遇到了新的字符,就要重新計算最長無重復子串。最后每次都要在哈希表中將當前字符對應的值賦值為i+1。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.empty())
            return 0;
        vector<int> m(256,0);
        int res = 0,left = 0;
        for(int i=0;i<s.size();++i)
        {
            left = max(left,m[s[i]]);
            m[s[i]] = i+1;//記錄的位置都要+1,以區別默認的0 
            res = max(res,i-left+1);
        }
        return res;
    }
};

 方法二:使用hash表記錄位置

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.empty())
            return 0;
        unordered_map<char,int> m;
        int res = 0,left = 0; // 由於map不能初始化,所以默認為0
        for(int i=0;i<s.size();++i)
        {
            left = max(left,m[s[i]]);
            m[s[i]] = i+1; //此處記錄的是第幾個的位置
            res = max(res,i-left+1);//上面加1了,此處就要-1;
        }
        return res;
    }
};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM