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