zoukankan      html  css  js  c++  java
  • 代码题(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<<"
    " << 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;
        }
    };
  • 相关阅读:
    let 及const
    ES6与ES2015、ES2016以及ECMAScript的区别
    AMD CMD commonJS es6
    千里之行,始于足下
    学习随笔 pyspark JDBC 操作oracle数据库
    学习随笔--pyspark RDD常用操作
    学习随笔--Spark java开发入门
    学习随笔--flask框架基本搭建
    学习随笔--scrapy爬虫简单实例
    学习随笔-python动态爬取空气质量网数据的实现
  • 原文地址:https://www.cnblogs.com/eilearn/p/9541232.html
Copyright © 2011-2022 走看看