题目
有奖征名
题目描述
MTK 内部每款芯片都会有一个名字,比如 Oppo R15 中采用的 AI 芯片 HelioP60。现在公司即将量产一块芯片,为此在内部举办了征名比赛,命名的规则要求简洁,请你找出员工提交的命名中不包含重复字符的最长子串作为最后的候选结果,如果存在多个符合条件的子串,则输出第一个符合条件的子串。 例如输入一个字符串为:helioP60isisGood ,该字符串中满足要求的子串为 helioP60。 因此输出该子串。
输入说明
通过命令行参数方式传入一个字符串,且只包含数字和字母(区分大小写)。
int main(int argc, char argv[])
{
char input = argv[1];
……
return 0;
}
输出说明
向标准输出打印要求的结果字符串
示例
输入
helioP60isisGood
输出
helioP60
思路
- 首先肯定是暴力,把每个子串都判断一下,这样的话这篇文章就没必要写了~
- 我们有复杂度为O(n)的算法,那就是滑动窗口。
- 假设 Sij S i j 表示从 i 到 j 已经访问过的字符,刚开始i=j=0,i 与 j 都是可以往后滑动的。当我们再遍历到 j+1 的时候,我们会在 Sij S i j 中寻找是否有这个字符,如果有,并假设其索引为 j’ ,我们将 i 滑至 j’+1,则此时 Sij S i j 内字符并不重复。中间记录一下状态即可。
- 而 i 怎么滑动到 j’+1,使用不同的数据结构会有不同的滑动方式。
代码
#include <iostream>
#include <string>
#include <set>
#include <map>
using namespace std;
// 滑动窗口,O(2n)
int main(int args, char** argc) {
char* s = argc[1];
//char* s = "helioP60isisGOOD";
int len = strlen(s);
set<char> set;
int max = 0, left = 0, right = len - 1;
int i = 0, j = 0;
while (i < len && j < len) {
if (set.find(s[j]) == set.end()) {
set.insert(s[j]);
if (max < j - i + 1) {
left = i, right = j + 1;
max = j - i + 1;
}
j++;
}
else {
set.erase(s[i++]);
}
}
cout << string(s).substr(left, max) << endl;
system("pause");
return 0;
}
用map存储的话,就可以直接让 i 跳至 j’+1,只需遍历一遍:
#include <iostream>
#include <string>
#include <set>
#include <map>
using namespace std;
// 优化的滑动窗口,O(n)
int main(int args, char** argc) {
char* s = argc[1];
//char* s = "s6ws";
int len = strlen(s);
map<char, int> map;
int max = 0, left = 0, right = len - 1;
int i = 0, j = 0;
for (; j < len; j++) {
if (map.find(s[j]) != map.end()) {
i = map[s[j]] + 1;
}
if (max < j - i + 1) {
left = i, right = j + 1;
max = j - i + 1;
}
map[s[j]] = j;
}
cout << string(s).substr(left, max) << endl;
system("pause");
return 0;
}