题目如标题。求最长子串,而不是子串长度。
分析过程:
最终目标:找到最长的无重复字符字串的开始位置和结束位置。
准备工作:
①定义两个长度为256的数组,分别用于保存各个字符上一次出现的位置和标记各个字符是否已出现过。
②定义两组位置数据,每一组分别是两个变量用于保存开始位置beg、结束位置end,并初始化为0。(先使用beg1、end1)
过程:
①遍历输入字符串input,假设当前索引为index。
②判断当前字符是否已存在,如果存在,比较end1 - beg1与end2 - beg2的值,如果大于,则交换beg1和beg2、end1和end2,然后将从当前字符上一次出现的位置开始,一直到end2位置之间的所有字符出现的标志都置位false,再将beg1置位当前字符上一次出现的位置+1,最后从beg1到当前位置之间的字符出现标志都置位true。
③将当前字符的出现标志值为true,并保存出现的位置为当前位置index。
④记录当前结束位置end1为index + 1.
⑤遍历下一个字符,重复②~⑤,直到结束。
⑥到此,对比end1 - beg1与end2 - beg2,如果大于,则取input中beg1~end1之间的字符串;否则取input中beg2~end2之间的字符串。
假设输入序列长度为N,则时间复杂度为O(N),空间复杂度为O(1)。
编译环境:C++11
源码实现:
#include <string>
std::string longest_substring(const std::string &input)
{
long main_beg = 0, backup_beg = 0;// 开始位置
long main_end = 0, backup_end = 0;// 结束位置(不包含)
long char_pos[256]; // 保存各个字符上一次出现的位置
bool char_exists[256]{false}; // 保存各个字符是否已存在
long index = 0;
for(const char ch : input)
{
long char_index = static_cast<long>(ch);
if(char_exists[char_index])
{
// 获得较短的那一个作为main
if(main_end - main_beg > backup_end - backup_beg)
{
std::swap(main_beg, backup_beg);
std::swap(main_end, backup_end);
}
for(long i = char_pos[char_index] - 1; i >= backup_beg; --i)
{
if(!char_exists[static_cast<long>(input[i])])
{ break; }
char_exists[static_cast<long>(input[i])] = false;
}
main_beg = char_pos[char_index] + 1;
for(long i = main_beg; i < index; ++i)
{
char_exists[static_cast<long>(input[i])] = true;
}
}
char_exists[char_index] = true;
char_pos[char_index] = index++;
main_end = index;
}
if(main_end - main_beg > backup_end - backup_beg)
{
return std::string(input.begin() + main_beg, input.begin() + main_end);
}
return std::string(input.begin() + backup_beg, input.begin() + backup_end);
}
测试代码:
#include <iostream>
int main()
{
std::cout << longest_substring("loiugelajdlasfd") << std::endl;
system("pause");
return 0;
}
运行结果: