首页 > 代码库 > Leetcode:Longest Substring Without Repeating Characters
Leetcode:Longest Substring Without Repeating Characters
题目大意是传入一条字符串,计算出这样的这样一条子字符串,要求子字符串是原字符串的连续的某一段,且子字符串内不包含两个或两个以上的重复字符。求符合上面条件的字符串中最长的那一条的长度。
首先注意到任意一条无重复子字符串的任意子字符串应该也满足无重复这一特性。因此这个问题可以用动态规划解决。
需要先计算出字符串s的每个元素对应的首个重复字符的下标,字符串中下标为i的元素的首个重复字符的下标j应该满足j > i && s[i] == s[j],且是满足这些条件中的最小值。比如asaa中下标为0的元素对应的首个重复字符的下标为2,尽管3也满足条件,但不是最小值。
计算的方式如下:
registries = int[256]
nextOccurance = int[s.length]
initialize elements in registries with -1 //将所有registries 中的元素都初始化为-1
initialize elements in nextOccurance with s.length //将所有registries 中的元素都初始化为s.length
for(i = 0; i < s.length; i++)
c = s[i]
if registries[c] != -1 then
nextOccurance [registries[c]] = i
registries[c] = i
这里用注册表registries的注册项registries[c]记录当前对字符c的查找请求,而nextOccurance的元素nextOccurance[i]表示s[i]的首个重复字符子s中的下标。每次循环时,都会检测注册表项registries[c]是否以及被注册,如果注册则为nextOccurance [registries[c]]记录当前下标i。最后则将之前的注册项清除,而用当前下标作为新的请求项。
之后使用动态规划解决问题。利用数组longestLengthes,longestLengthes[i]表示所有以下标i作为起始的无重复子字符串的最长长度。显然longestLengthes[s.length - 1]应该为1。而对于任意i < s.length - 1,应该有longestLengthes[i] = min(longestLengthes[i + 1] + 1, nextOccurance [i] - i)。其中longestLengthes[i + 1] + 1表示理想状态下(不考虑出现多个longestLengthes[i]对应的字符)的最优长度,而nextOccurance [i] - i表示可能的以下标i作为起始的无重复子字符串的最长长度,因为s[i] == s[nextOccurance [i]],这里已经发生了重复。之所以敢保证longestLengthes[i] <= longestLengthes[i + 1] + 1是因为假如longestLengthes[i]>longestLengthes[i + 1] + 1,那么显然longestLengthes[i + 1] >= longestLengthes[i] - 1 >longestLengthes[i + 1](整体不重复自然局部不会重复),这是不可能发生的。(如果对这部分不理解,可以枚举可能的情况,总共只有三种)转换成代码:
longestLengthes = int[s.length]
longestLengthes[s.length - 1] = 1
for(i = s.length - 2; i >= 0; i--)
longestLengthes[i] = min(longestLengthes[i + 1] + 1, nextOccurance [i] - i)
之后遍历整个longestLengthes数组就可以找到最长的无重复子字符串的长度。
把上面两部分的代码整合,可以轻易得出整体的复杂度为O(n),其中n为传入字符串的长度。
最后给出整个实现代码,给有需要的人:
package cn.dalt.leetcode; /** * Created by Administrator on 2017/6/4. */ public class LongestSubstringWithoutRepeatingCharacters { public static void main(String[] args) { String s = "yiwgczzovxdrrgeebkqliobitcjgqxeqhbxkcyaxvdqplxtmhmarcbzwekewkknrnmdpmfohlfyweujlgjf"; System.out.println(new LongestSubstringWithoutRepeatingCharacters().lengthOfLongestSubstring(s)); for(char c = 0; c < 256; c++) { System.out.println((int)c + ":" + c); } } int[] nextOccurIndexes = null; int[] registries = new int[1 << 8]; public int lengthOfLongestSubstring(String s) { //Calculate all next occur indexes //nextOccurIndexes[i] is the minimun index of s which has properties that index > i && s[i] = s[index] int slength = s.length(); if (slength == 0) { return 0; } nextOccurIndexes = new int[s.length()]; for (int i = 0, bound = registries.length; i < bound; i++) { registries[i] = -1; } for (int i = 0, bound = s.length(); i < bound; i++) { int c = s.charAt(i); int registry = registries[c]; if (registry != -1) { nextOccurIndexes[registry] = i; } registries[c] = i; } for (int registry : registries) { if (registry != -1) { nextOccurIndexes[registry] = slength; } } int[] longestNoneRepetitionSubstringLengthes = new int[s.length()]; longestNoneRepetitionSubstringLengthes[s.length() - 1] = 1; int longestNoneRepetitionSubstringIndex = s.length() - 1; for (int i = s.length() - 2; i >= 0; i--) { int probablyMaxLength1 = longestNoneRepetitionSubstringLengthes[i + 1] + 1; int probablyMaxLength2 = nextOccurIndexes[i] - i; longestNoneRepetitionSubstringLengthes[i] = Math.min(probablyMaxLength1, probablyMaxLength2); longestNoneRepetitionSubstringIndex = longestNoneRepetitionSubstringLengthes[i] > longestNoneRepetitionSubstringLengthes[longestNoneRepetitionSubstringIndex] ? i : longestNoneRepetitionSubstringIndex; } return longestNoneRepetitionSubstringLengthes[longestNoneRepetitionSubstringIndex]; } }
Leetcode:Longest Substring Without Repeating Characters