1. 程式人生 > >代碼題(56)— 最長重復子串、無重復字符的最長子串

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

cto wke 新的 pan pty shm 指針數組 map hashmap

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;
    }
};

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