代码题(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;
    }
};

 

posted @ 2018-08-27 11:32  深度机器学习  阅读(3696)  评论(0编辑  收藏  举报