问题描述:https://leetcode.com/problems/longest-substring-without-repeating-characters/

思路1:分治策略

按照算法导论上的分治算法框架强行写的。

class CommonUtil {
static lightCopy(obj) {
let result = {}
Object.assign(result, obj);
return result;
}
} // O(???), Runtime: 7792 ms, faster than 1.00% ...
var lengthOfLongestSubstring = function(s) {
if (s == "") return 0; let buildMaps = (symbol, p, q, r) => {
let map = [],
length = 0;
let current = () => q - length;
if (symbol == "right") {
current = () => q - 1 + length;
} let init = () => { // 创建一个空对象,免去繁琐的初始化过程
map[length++] = {};
}; let isSafe = () => { // 越界or字符重复返回false
let out = symbol == "right" ? current() >= r : current() < p;
let repeat = map[length - 1][s[current()]];
return !out && !repeat;
}; let createMap = () => { // 复制map>>添加元素>>为下次调用做准备
map[length] = CommonUtil.lightCopy(map[length - 1]);
map[length][s[current()]] = true;
length++;
}; init();
while (isSafe()) {
createMap();
}
return map;
}; let combineMaps = (leftMaps, rightMaps, max) => {
let compatible = (map1, map2) => {
if (map1.length < map2.length) {
let temp = map1;
map1 = map2;
map2 = temp;
}
let keys = Object.keys(map2);
return !keys.some(x => map1[x]);
}; let getPairs = (totalReduce) => {
let reducePairs = [];
for (let k = 0; k <= totalReduce; ++k) {
reducePairs.push({
i: k,
j: totalReduce - k,
});
}
return reducePairs;
}; const leftLen = leftMaps.length - 1,
rightLen = rightMaps.length - 1,
baseLen = leftLen + rightLen; let sum = 0;
let totalReduce = 0;
while ((sum = baseLen - totalReduce) > max) {
// 只检测比子问题解的值更大的情形,由大往小找,一旦找到立刻停止搜索
let reducePairs = getPairs(totalReduce);
for (let k = 0; k != reducePairs.length; ++k) {
let i = leftLen - reducePairs[k].i;
let j = rightLen - reducePairs[k].j;
if (i > 0 && j > 0 && compatible(leftMaps[i], rightMaps[j])) {
return sum;
}
}
totalReduce++;
}
return max;
}; let findMaxCrossingSubstring = (p, q, r, subMaxLength) => {
if (s[q - 1] == s[q]) { // 不可连接
return subMaxLength;
} else {
let leftMaps = buildMaps("left", p, q, r);
let rightMaps = buildMaps("right", p, q, r);
return combineMaps(leftMaps, rightMaps, subMaxLength);
}
}; let findMax = (start, end) => {
if (start + 1 < end) {
let mid = Math.floor((start + end) / 2); // Divide
let maxLeft = findMax(start, mid); // Conquer
let maxRight = findMax(mid, end); // Conquer
const subMaxLength = Math.max(maxLeft, maxRight); // Combine
return findMaxCrossingSubstring(start, mid, end, subMaxLength); // Combine
} else {
return 1; // basecase
}
}; return findMax(0, s.length);
};

思路2:Brute Force - O(n^3)

js代码如下:

let satisfyCondition = (i, j, s) => {
let map = {};
for (let k = i; k <= j; ++k) {
if (map[s[k]]) return false;
map[s[k]] = true;
}
return true;
}; // O(n^3), Runtime: 880 ms, faster than 6.67% ...
var lengthOfLongestSubstring2 = function(s) {
if (s == "") return 0;
let max = 1,
length, i, j;
for (i = 0; i != s.length; ++i) {
for (j = i + 1; j != s.length; ++j) {
length = j - i + 1;
if (length > max) {
if (satisfyCondition(i, j, s)) {
max = length;
} else {
break;
}
}
}
}
return max;
};

思路3:动态规划?

最开始思路有点离谱,于是记录下写了n个版本的心路历程。

O(n^2),错误思路:420 ms

原始思路:完全依靠一个动态的hashMap的map[key]操作判断当前字符与上一个最长子串的兼容性。(每一个这样的“上一个子串”都对应一个全新、特定的hashMap)

js代码如下:

var lengthOfLongestSubstring3 = function(s) {
let m = [], // 保存最优解的值,以长度作为下标
b = [], // 保存长度为i时,new的值
p, r, map = {}; let init = () => {
b[0] = 0;
m[0] = 0;
}; let work = () => {
for (let len = 1; len <= s.length; ++len) {
r = len - 1; // 引入r:将子问题规模len转化为当前字符的下标
if ((p = map[s[r]]) == undefined) { // 兼容直接+1
map[s[r]] = r;
b[len] = b[len - 1] + 1;
} else { // 不兼容重新计算
map = {};
for (let i = p + 1; i <= r; ++i) {
map[s[i]] = i;
}
b[len] = r - p;
}
m[len] = Math.max(m[len - 1], b[len]);
}
}; init();
work();
return m[s.length];
};

因为规模为n的问题实际仅依赖于规模为n-1的子问题,所以只需保存上一轮迭代的相关值就行了。

O(n)版,思路转变: 100 ms

before:

  1. 判定兼容性:仅用map[key]判断
  2. 维护hashMap:兼容添加键值对,不兼容重新创建hashMap并添加相应的键值对
  3. 在如何高效地动态修改hashMap上钻牛角尖

after:

  1. 判定兼容性:map[key]判断+利用map[key]里存储的下标,同时维护“上一个最长子串的起始下标”构造一个简单的判定条件
  2. 维护hashMap:兼容添加键值对,不兼容更新相应键的值就可以了(对于JS而言,两个代码都一样)
  3. 通过添加条件而不是修改hashMap,筛掉对hashMap的无效命中

代码如下:

var lengthOfLongestSubstring6 = function(s) {
let m = 0, lastIndex = {}, lastBegin = 0, currentIndex;
for (let len = 1; len <= s.length; ++len) {
currentIndex = len - 1;
let ch = s[currentIndex];
if (lastIndex[ch] !== undefined && lastIndex[ch] >= lastBegin) { // 不兼容
lastBegin = lastIndex[ch] + 1;
}
lastIndex[ch] = currentIndex;
m = Math.max(m, currentIndex - lastBegin + 1);
}
return m;
};

语言层面的些许优化

实际运行效果最好。

var lengthOfLongestSubstring7 = function(s) {
if (s.length < 2) return s.length;
let max = 0, lastBegin = 0, code, characterIndex = new Array(255), fresh;
for (let i = 0; i !== s.length; ++i) {
code = s.charCodeAt(i);
if (characterIndex[code] >= lastBegin) {
lastBegin = characterIndex[code] + 1;
}
fresh = i - lastBegin + 1;
if (fresh > max) {
max = fresh;
}
characterIndex[code] = i;
}
return max;
};

js代码性能优化的小结:

  1. 缩短变量名绝对是最得不偿失的做法!大大降低可读性的同时,对性能的提升几乎为0(在网络上传输有专门的精简代码工具)
  2. 将函数手动内联同样不能提升性能(约等于0)
  3. 用if语句取代m = Math.max(m, xxxx);当m>xxxx成立时,采用if语句可以省去一次不必要的赋值。
  4. 当数组长度能确定在某个范围时用new Array(size)取代[]
  5. 当打算用HashMap的时候,以数字作为键的数组>obj>new Map()
  6. 尽量不要用类似if(变量)作为条件判断,因为有时0可能是变量的可行值之一,但是会被转化成false
  7. 尽量用===取代==,避免转型出错,似乎对性能还有微乎其微的帮助???。。。。
  8. 显式地对null、undefined、NaN等值进行判断,可以减少一些莫名其妙的bug
  9. undefined与任何数比较都返回false

LeetCode #003# Longest Substring Without Repeating Characters(js描述)的更多相关文章

  1. 【JAVA、C++】LeetCode 003 Longest Substring Without Repeating Characters

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  2. [Leetcode]003. Longest Substring Without Repeating Characters

    https://leetcode.com/problems/longest-substring-without-repeating-characters/ public class Solution ...

  3. C++版- Leetcode 3. Longest Substring Without Repeating Characters解题报告

    Leetcode 3. Longest Substring Without Repeating Characters 提交网址: https://leetcode.com/problems/longe ...

  4. 【LeetCode】003. Longest Substring Without Repeating Characters

    Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...

  5. LeetCode 3 Longest Substring Without Repeating Characters(最长不重复子序列)

    题目来源:https://leetcode.com/problems/longest-substring-without-repeating-characters/ Given a string, f ...

  6. LeetCode 3 Longest Substring Without Repeating Characters 解题报告

    LeetCode 第3题3 Longest Substring Without Repeating Characters 首先我们看题目要求: Given a string, find the len ...

  7. [LeetCode][Python]Longest Substring Without Repeating Characters

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com'https://oj.leetcode.com/problems/longest ...

  8. LeetCode之Longest Substring Without Repeating Characters

    [题目描述] Given a string, find the length of the longest substring without repeating characters. Exampl ...

  9. Leetcode 3. Longest Substring Without Repeating Characters (Medium)

    Description Given a string, find the length of the longest substring without repeating characters. E ...

随机推荐

  1. go关键字之struct定义声明方式

    type Student struct{ name string age int } var stu Student stu.name,stu.age = "张三”,10 stu2 := S ...

  2. 手写LRU算法

    import java.util.LinkedHashMap; import java.util.Map; public class LRUCache<K, V> extends Link ...

  3. Webpack前端打包工具

    一.安装 安装Webpack之前需要安装nodejs,然后用npm安装: $ npm install webpack -g &nsbp;运行以上命令就将Webpack安装到了全局环境中.  但 ...

  4. Golang知识图谱

    原:https://www.processon.com/view/link/5a9ba4c8e4b0a9d22eb3bdf0?from=timeline 百度脑图重制:http://naotu.bai ...

  5. lxml xpath 爬取并正常显示中文内容

    在使用python爬虫提取中文网页的内容,为了能正确显示中文的内容,在转为字符串时一定要声明编码为utf-8,否则无法正常显示中文,而是显示原编码的字符,并没有正确转换.比如下面这个简单的爬取百度页面 ...

  6. php项目,别人无法访问自己(windows 系统)上Apache服务器原因(转载)

    别人无法访问自己电脑上的Apache服务器,其中最大的原因是因为Windows防火墙的因素. 1.有安装防火墙的,把防火墙关闭 2.windows默认带防火墙的,进入 控制面板-系统和安全-Windo ...

  7. java核心技术笔记

    1.类和对象 第四章:面向对象 日历的作用是提供某个时间点的信息 查询设置信息:GregorianCalendar now = new GregorianCalendar() int month = ...

  8. 静态方法(staticmethod)和类方法(classmethod)

    类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被类调用,就像正常调用函数一样 类方法和静态方法的相同点:都可以直接被类调用, ...

  9. jq1.9.0以上版本不兼容live()解决方法

    最近一个项目里用bootstrap做图形渲染,需要用到jq1.9以上版本,而copy的js代码里用到了live()方法,故两者产生了兼容问题,下面是解决方案: $('#my').on("cl ...

  10. JAVA期末考试整理

    Technical problem: 0.read: Scanner input= new Scanner(System.in) random#: x=(int)(Math.random()*10) ...