LeetCode week 2 : Longest Substring Without Repeating Characters

来源:互联网 发布:什么数据库管理系统 编辑:程序博客网 时间:2024/06/15 19:25

题目

地址:
https://leetcode.com/problems/longest-substring-without-repeating-characters/description/
类别: 动态规划
难度: Medium
描述:
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given “abcabcbb“, the answer is “abc“, which the length is 3.
Given “bbbbb“, the answer is “b“, with the length of 1.
Given “pwwkew“, the answer is “wke“, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

分析

题目是要求找出不含重复字符的最长子字符串,我们记子字符串的起点位置为head,结束位置为tail。对此我们最暴力的解法就是找出以所有位置字符为head的不含重复字符子序列,然后对其长度进行比较得到最长的子序列长度。显然这样的时间复杂度为O(n^2),比较费时。
我们可以开始考虑另一个思路,首先从字符串的起始位置开始作为子序列的起点head,依次向后遍历,同时记录当前子字符串的长度,如果遍历到了已经在当前子字符串出现过的重复字母,则找出当前子字符串中重复字母的位置,并将此位置的下一个字符作为起点head继续找子字符串。不断重复上述过程并更新目前最长字符串长度。其中主要的原理将通过下面的例子来阐述:
字符串:abcdefdhijkd
第一轮:起点为a

字符串 当前最长子字符串长度 a 1 ab 2 abc 3 abcd 4 abcde 5 abcdef 6

此时我们遍历到d,这个字符已经出现在之前的子字符串里,此时我们需要将起点重置为e。为什么需要将起点重置在这里呢?我们之前的暴力解法是以每个位置为起点都遍历一遍,而这明显是做了许多不必要的工作的。
首先我们考虑a开头的子序列,在遍历到f的时候我们就停止查找以a开头的子序列了,因为此时以a开头的不重复子序列已经查找完毕,再以a为起点向后查找就因为有重复的d而不符合条件。
在查完以a开头的子字符串后,如果按照暴力解法是又以b为起点开始遍历,而我们却直接将起点head跳到了e处。这是因为以b或c或第一个d(即上一起点与第一个d下一个位置之间的字符)为开头的最长子字符串长度都必定小于以a开头的最长子字符串长度(这是显而易见的,因为它们都因为第二个d的出现而终止查找,但a起点更靠前)。
因此我们可以放心地跳过上一起点与重复字母下一个位置之间的字符而直接到e开始进入第二轮。
此外,第二轮的以e为head的查找中,我们也可以将第二轮的tail直接从第二个d开始,原理与之类似,以e或f为tail的最长子字符串长度都必定小于以d为tail的最长子字符串长度。
第二轮:

字符串 当前最长子字符串长度 efd 6 efdh 6 efdhi 6 efdhij 6 efdhijk 7 (此时大于之前最长,更新为7)

此时又出现了d,同理现在以f和第二个d为起点的子字符串最大长度必定小于以e开头的,所以起点head直接跳至h,终点tail直接跳至第三个d。
第三轮:

字符串 当前最长子字符串长度 hijkd 7

最后得到的最长子序列长度为7。

代码实现

class Solution {public:    int lengthOfLongestSubstring(string s) {        vector<int> dict(256, -1);    //存储字符访问信息        int maxlen = 0;        int head = 0;        int tail = 0;        for(; tail < s.length(); tail++) {            /*            dict[s[tail]] >= head说明当前尾字符已经在head至tail间出现,而head之前的字符不会对其产生影响。             */            if(dict[s[tail]] >= head ) {                 head = dict[s[tail]] + 1;    //重置head至前重复字符者下一个位置,tail则不变(即直接跳到了后重复字符位置)。            }            dict[s[tail]] = tail;            maxlen = max(maxlen, tail - head + 1);        }        return maxlen;    }};

参考文献:
https://discuss.leetcode.com/topic/24739/c-code-in-9-lines
http://www.ituring.com.cn/article/195597

阅读全文
0 0