给定一个字符串,找到最长子字符串的长度而不重复字符。例如,没有重复“abcabcbb”字母的最长子字符串是“abc”,长度为3.对于“bbbbb”,最长的子字符串是“b”,长度为1.
public static int lengthOfLongestSubstring(String s) {
if (s.length()==0)
return 0;
int maxlen = 1;
HashMap<Character, ArrayList<Integer>> check = new HashMap<Character,ArrayList<Integer>>();
for (int i = 0; i < s.length(); i++) {
for (int j = i; j < s.length(); j++) {
if (!check.containsKey(s.charAt(j))) {
ArrayList<Integer> value= new ArrayList<>();
value.add(j);
check.put(s.charAt(j), value);
}
else {
maxlen = Math.max(j - i, maxlen);
ArrayList<Integer> temp = check.get(s.charAt(j));
i=temp.get(temp.size()-1);
// get the last index(biggest index) of the key value
check.clear();
break;
}
if(j==s.length()-1) {
maxlen = Math.max(j - i + 1, maxlen);
}
}
}
return maxlen;
}
}
对于长可重复字符串的最后一次测试,超出了时间限制。不知道如何优化。寻求改进,谢谢
答案 0 :(得分:3)
这是一个相当简单的解决方案,应该更快地解决您的问题:
public static int longestNonRepeating(final String s) {
final Set<Character> unique = new HashSet<>();
int max = 0;
for (int i = 0; i < s.length(); ++i) {
final char c = s.charAt(i);
if (!unique.add(c)) {
for (int j = i - unique.size(); j < i; ++j) {
if (s.charAt(j) != c) {
unique.remove(s.charAt(j));
} else {
break;
}
}
}
max = Math.max(max, unique.size());
}
return max;
}
这是如何运作的?
我们沿String
走,并将字符添加到Set
。如果我们添加的字符已经包含在Set
中,那么我们知道当前子字符串中有一个副本。
在这种情况下,从当前子字符串的开头(必须与unique
的大小相同)开始,我们一起走。如果我们发现一个不是我们发现的重复的字符,副本必须更进一步,我们继续搜索。一旦我们找到副本,我们就可以停止搜索。
要对过程进行可视化:
a b c a b c
0 1 2 3 4 5
^
|
i
我们在a
唯一的Set
。{/ p>
a b c a b c
0 1 2 3 4 5
^
|
i
我们在a,b
唯一的Set
。{/ p>
a b c a b c
0 1 2 3 4 5
^
|
i
我们在a,b,c
唯一的Set
。{/ p>
a b c a b c
0 1 2 3 4 5
^ ^
| |
j i
我们尝试将add
a
添加到唯一的Set
,这是重复的。从唯一子字符串的开头,尝试找到a
。幸运的是,这是0
,我们不需要从唯一中删除任何内容。
a b c a b c
0 1 2 3 4 5
^ ^
| |
j i
我们尝试将add
b
添加到唯一的Set
,这是重复的。从唯一子字符串的开头,尝试找到b
。幸运的是,这是1
,我们不需要从唯一中删除任何内容。
a b c a b c
0 1 2 3 4 5
^ ^
| |
j i
我们尝试将add
c
添加到唯一的Set
,这是重复的。从唯一子字符串的开头,尝试找到c
。幸运的是,这是1
,我们不需要从唯一中删除任何内容。
我们已经完成了。最长的唯一子字符串是3
。
答案 1 :(得分:2)
以下是时间复杂度为O(n)
的解决方案。由于两个原因,它可能比其他解决方案更快。
它仅比较链断开时或当达到String
结束时不同连续字符链的长度与当前最大值。
通过跟踪每个字符的最后一个索引而不是
子串中的字符集,从来没有任何理由
删除任何元素。这当然意味着如果String
有许多不同的字符,它会占用大量内存。
public static int subStringLength(final String s) {
final Map<Character, Integer> indices = new HashMap<>();
int max = 0;
int start = 0;
final int length = s.length();
for (int i = 0; i < length; i++) {
Integer k = indices.put(s.charAt(i), i);
if (k != null && k >= start) {
max = Math.max(max, i - start);
start = k + 1;
}
}
return Math.max(max, length - start);
}
答案 2 :(得分:1)
这个问题可以在O(N)中解决,其中N是字符串的长度。
算法:
1)迭代字符串的字符。跟踪每个角色的最后一次出现。在每个信件商店,前一个相同的字符有多远。 E. g。:有字符串“abccdae”,我们会得到列表[1,2,3,1,5,5,7]。请注意,如果在我们将字符设置为长度到单词的开头之前没有出现过字符。
2)让我们调用我们得到V的列表(例如V = [1,2,3,1,5,5,7])。
3)定义函数f(x),它计算最长的单词而不重复以索引x结尾的字符。
它认为: f(0)= 0 f(x)= min(f(x-1)+1,V [x]),x> 0
4)迭代单词并在每个索引处计算f。
5)找到最大值f。
每一步都是O(N),但如果你玩的话,你可以同时做所有这些,甚至不变。
希望这有帮助。
答案 3 :(得分:1)
在下面找到优化版本。与初始版本相比的增强功能:
edit2 我上传了一个JMH基准here,它比较了这个问题的三个答案的算法。直接链接到benchmark result。
public static void main(String[] args) {
String[] strings = {"abcabcdebb", "abcbacde", "abb", "bba",
"cbaabc", "abccba", "xabccba", "abcxcba", "abccbax",
"", "a", "aa", "ab"
};
for (String s : strings) {
System.out.printf("string: %-10s maxSubStringLength: %d%n", s,
maxSubStringLength(s));
}
}
static int maxSubStringLength(String string) {
if (string.isEmpty()) {
return 0;
}
int maxLength = 1;
int low = 0;
for (int high = 1; high < string.length(); high++) {
for (int pos = high - 1; pos >= low; pos--) {
if (string.charAt(pos) == string.charAt(high)) {
low = pos + 1;
break;
}
}
maxLength = Math.max(maxLength, high - low + 1);
if (string.length() - low <= maxLength) {
break;
}
}
return maxLength;
}
如何运作
low
high
的指针增加到字符串high
指针,我们会向后搜索,如果最右边的字符(位置high
上的那个字符)出现在从索引low
到high
的子字符串中首发帖子,看看代码的演变
一个非常务实的解决方案可能是
public static void main(String[] args) {
String s = "abcabcbb";
int maxLength = 1;
for (int i = 0; i < s.length(); i++) {
for (int j = i+2; j <= s.length(); j++) {
String substring = s.substring(i, j);
if (hasNoDupeChars(substring) && substring.length() > maxLength) {
System.out.println("substring = " + substring);
maxLength = substring.length();
}
}
}
System.out.println("maxLength = " + maxLength);
}
private static boolean hasNoDupeChars(String substring) {
Set<Character> chars = new HashSet<>();
for (Character c : substring.toCharArray()) {
if (!chars.add(c)) {
return false;
}
}
return true;
}
编辑正如鲍里斯所提到的,仍然可以进行优化。在Donald E. Knuth之后我不会这样做:&#34;过早的优化是所有邪恶的根源。&#34; ; - )