Longest Substring without repeating characters issue with edge case - string

I was trying to solve this problem: Longest substring without repeating characters. The issue is, it's failing in couple test cases, I don't know how to fix it. I would need your help to see where I'm going wrong.
Question:
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.
This is my code:
function longestSubString(arr){
let localSum=0,globalSum=0;
let set = new Set();
for(let i=0; i<arr.length; i++){
let current = arr[i];
//if the key is present in the store.
if(set.has(current)){
set.clear();
localSum = 1;
set.add(current);
} else {
localSum +=1;
set.add(current);
}
if(globalSum < localSum){
globalSum = localSum;
}
}
return globalSum;
}
Tests:
let test = "abcabc"; //returns 3 - correct
let test2 = "bbb"; //returns 1 - correct
let test5 = "dvdf"; //returns 2 - INCORRECT! it should return 3 (i.e for vdf) since I'm doing set.clear() I'm not able to store previous elements.
longestSubString(test5); //incorrect
Live:
https://repl.it/Jo5Z/10

Not fully tested!
function longestSubString(arr){
let localSum=0,globalSum=0;
let set = new Set();
for(let i=0; i<arr.length; i++){
let current = arr[i];
//if the key is present in the store.
if(set.has(current)){
let a = Array.from(set);
a.splice(0,a.indexOf(current)+1);
set = new Set(a);
set.add(current);
localSum = set.size;
} else {
localSum +=1;
set.add(current);
}
if(globalSum < localSum){
globalSum = localSum;
}
}
return globalSum;
}
The idea is that when you get duplicate, you should start from the charachter after the first duplicated character, in your case dvdf, when you reach the second d you should continue from vd not from d!

You have to consider that the substring might start from any character in the string. Erasing the set only when you're finding a duplicate makes you only consider a substring starting from characters that are equal to to the first character.
An O(logn*n^2) solution modifying yours just a bit:
function longestSubString(arr){
let globalSum=0;
for(let i=0; i<arr.length; i++){
let set = new Set();
let localSum=0;
for(let j=i; j<arr.lenght; j++){
let current = arr[j];
//if the key is present in the store.
if(set.has(current)){
break;
} else {
localSum +=1;
set.add(current);
}
}
if(globalSum < localSum){
globalSum = localSum;
}
}
return globalSum;
}
There's also a O(n + d) (almost linear) solution, d being the number of characters in the alphabet. See http://www.geeksforgeeks.org/length-of-the-longest-substring-without-repeating-characters/.

There seem to be a lot of long answers here. The implementation I've thought of is simplified due to two observations:
Whenever you encounter a duplicate character, you need to start the next substring just after the previous occurrence of the current character.
Set() creates an array in insertion order when iterated.
function longestSubstring(str) {
let maxLength = 0
let current = new Set()
for (const character of str) {
if (current.has(character)) {
const substr = Array.from(current)
maxLength = Math.max(maxLength, substr.length)
current = new Set(substr.slice(substr.indexOf(character) + 1))
}
current.add(character)
}
return Math.max(maxLength, current.size)
}
const tests = [
"abcabc",
"bbb",
"pwwkew",
"geeksforgeeks",
"dvdf"
]
tests.map(longestSubstring).forEach(result => console.log(result))
A simple edit allows us to keep the first occurrence of the largest substring instead of the maximum length.
function longestSubstring(str) {
let maxSubstr = []
let current = new Set()
for (const character of str) {
if (current.has(character)) {
const substr = Array.from(current)
maxSubstr = maxSubstr.length < substr.length ? substr: maxSubstr
current = new Set(substr.slice(substr.indexOf(character) + 1))
}
current.add(character)
}
const substr = maxSubstr.length < current.size ? Array.from(current) : maxSubstr
return substr.join('')
}
const tests = [
"abcabc",
"bbb",
"pwwkew",
"geeksforgeeks",
"dvdf"
]
tests.map(longestSubstring).forEach(result => console.log(result))
As we can see, the last test yields vdf, as expected.

Below solution gets the length in O(n+d) time and also prints the longest non repeating character substring as well:
public void longestNonRepeatingLength(String a){
a="dvdf";
int visitedIndex[] = new int[256];
int curr_len = 0, max_len = 0, prev_ind = 0, start = 0, end = 1;
for(int i =0;i<256;i++)
visitedIndex[i] = -1;
visitedIndex[a.charAt(0)] = 0;
curr_len++;
int i = 0;
for( i=1;i<a.length();i++){
prev_ind = visitedIndex[a.charAt(i)];
if(prev_ind == -1 || i > prev_ind + curr_len)
curr_len++;
else{
if(curr_len>max_len){
start = prev_ind + 1;
end = i;
max_len = curr_len;
}
curr_len = i - prev_ind;
}
visitedIndex[a.charAt(i)] = i;
}
if(curr_len>max_len){
end = i-1;
max_len = curr_len;
}
for( i = start;i<=end;i++)
System.out.print(a.charAt(i));
System.out.println("");
System.out.println("Length = "+max_len);
}

As set contains the largest set of non-repeating characters of a String ending on index i, this means that when you encounter a previously seen character, rather than starting over with an empty set as your codes does now, you should just remove all characters from your set until that duplicate one.
Say for example your input is "abXcXdef". When the second "X" is encountered, you'll want to drop "a" and "b" from your set, leaving a set of ("c","X") as the longest set up to that point. Adding all other characters (as none are duplicates) you then end up with a max length of 5.
Something like this should work:
function longestSubString(arr) {
let globalSum = 0;
let set = new Set();
for (let i=0; i<arr.length; i++) {
let current = arr[i];
if (set.has(current)) {
while (true) {
let removeChar = arr[i - set.count];
if (removeChar != current)
set.remove(removeChar);
else
break;
}
} else {
set.add(current);
if (set.count > globalSum)
globalSum = set.count;
}
}
return globalSum;
}
As every character is added at most once and deleted at most once, this is an O(N) algorithm.

Related

LC: Concatenated Words

LC Question: https://leetcode.com/problems/concatenated-words/
var findAllConcatenatedWordsInADict = function(words) {
let m = new Map(), memo = new Map();
let res = [];
for (let i = 0; i < words.length; i++) {
m.set(words[i], 1);
}
for (let i = 0; i < words.length; i++) {
if (isConcat(words[i], m, memo)) res.push(words[i]);
}
return res;
};
function isConcat(word, m, memo) {
if (memo.has(word)) return memo.get(word);
for (let i = 1; i < word.length; i++) {
let prefix = word.slice(0, i);
let suffix = word.slice(i);
if (m.has(prefix) && (m.has(suffix) || isConcat(suffix, m, memo))) {
memo.set(word, true);
return true;
}
}
memo.set(word, false);
return false;
};
Still trying to wrap my head around the solution why do we call the isConcat function ONLY on the suffix that we generate from words[i] and not prefix?
Further, I have tried running various test cases ["cat","cats","dog","dogcatsdog","rat","ratcatdogcat"].
It seems like we do not call the isConcat function on the first suffix that is generated from word[0] ('at'). However, we do seem to call it on 'catsdog' as part of the "dogcatsdog" word so I'm not sure how the logic works as to how it chooses which suffixes to call on....
My mistake was misreading the code and assuming that the following conditions had to return true for a valid concatenated word:
Map has both prefix and suffix; OR
isConcat(suffix) returns true
In fact, prefix is already known to be in map (since map.has(prefix) must be true in order to reach the recursive call in the first place!). Thus, a concatenated word is only valid if the suffix is also an element of map or it's another concatenated word. map.has(suffix) tests if the suffix is in map, while isConcat(suffix, map, memo) tests whether the suffix is also a concatenated word.

finding the subsequence of strings in c# urjent

Alice has two strings, initial and goal. She can remove some number of characters from initial, which will give her a subsequence of that string. A string with no deletions is still considered a subsequence of itself. Given these two strings, can you find the minimum number of subsequences of initial that, when appended together, will form goal?
function minimumConcat(initial, goal) {
let result = 0;
let pattern = ''
let count1 = Array.apply(null, Array(26)).map(Number.prototype.valueOf, 0);
let count2 = Array.apply(null, Array(26)).map(Number.prototype.valueOf, 0);
initial.split('').forEach(c => {
pattern = pattern + c
});
pattern = "^[" + pattern + "]*$"
if (!RegExp(pattern).test(goal)) return -1
for (let i = 0; i < initial.length; i++) {
count1[initial.charCodeAt(i) - 97]++;
}
for (let i = 0; i < goal.length; i++) {
count2[goal.charCodeAt(i) - 97]++;
}
for (let i = 0; i < 26; i++) {
result += Math.abs(count1[i] - count2[i]);
}
return result;
}
var initial = readline();
var goal = readline();
print(minimumConcat(initial, goal));

AS3 String splitting keeping tags

I have variable strings that can contain tags, e.g.:
"<b>Water</b>=H<sub>2</sub>O"
What i want is an array that should look like:
Array[0] <b>Water</b>
Array[1] =H
Array[2] <sub>2</sub>
Array[3] O
How can i do this?
Thanks!
Give this a go, it uses a common regex to find html tags to get it separated and then indexOf and substring to assemble it back together.
function splitWithTags(str:String):Array {
var tags:Array = str.match(/(<(.|\n)*?>)/g);
var c:int = tags.length;
if(c%2 != 0) c--; // if it's odd, decrement to make even so loop works, last tag will be ignored.
if(c<2) return [str]; // not enough tags so return full string
var out:Array = [];
var end:int = 0;
var pos1:int, pos2:int;
for(var i:int = 0; i<c; i+=2) {
// find position of 2 tags.
pos1 = str.indexOf(tags[i]);
pos2 = str.indexOf(tags[i+1]);
if(pos1 >= end+1) { // there's some stuff not between tags to add
out.push(str.substring(end, pos1));
}
end = pos2+tags[i+1].length
out.push(str.substring(pos1, end));
}
if(end < str.length) { // there is substr at end
out.push(str.substring(end, str.length));
}
return out;
}

How to remove duplicates from a string except it's first occurence

I've been given the following exercise but can't seem to get it working.
//Remove duplicate characters in a
// given string keeping only the first occurrences.
// For example, if the input is ‘tree traversal’
// the output will be "tre avsl".
// ---------------------
var params = 'tree traversal word';
var removeDuplicates = function (string) {
return string;
};
// This function runs the application
// ---------------------
var run = function() {
// We execute the function returned here,
// passing params as arguments
return removeDuplicates;
};
What I've done -
var removeDuplicates = function (string) {
var word ='';
for(var i=0; i < string.length; i++){
if(string[i] == " "){
word += string[i] + " ";
}
else if(string.lastIndexOf(string[i]) == string.indexOf(string[i]))
{
word += string[i];
}
}
return word;
};
I'm not allowed to use replaceAll and when I create an inner for loop it doesn't work.
<script>
function removeDuplicates(string)
{
var result = [];
var i = null;
var length = string.length;
for (i = 0; i < length; i += 1)
{
var current = string.charAt(i);
if (result.indexOf(current) === -1)
{
result.push(current);
}
}
return result.join("");
}
function removeDuplicatesRegex(string)
{
return string.replace(/(.)(?=\1)/g, "");
}
var str = "tree traversal";
alert(removeDuplicates(str));
</script>
First of all, the run function should be returning removeDuplicates(params), right?
You're on the right lines, but need to think about this condition again:
else if(string.lastIndexOf(string[i]) == string.indexOf(string[i]))
With i = 0 and taking 'tree traversal word' as the example, lastIndexOf() is going to be returning 5 (the index of the 2nd 't'), whereas indexOf() will be returning 0.
Obviously this isn't what you want, because 't' hasn't yet been appended to word yet (but it is a repeated character, which is what your condition does actually test for).
Because you're gradually building up word, think about testing to see if the character string[i] exists in word already for each iteration of your for loop. If it doesn't, append it.
(maybe this will come in handy: http://www.w3schools.com/jsref/jsref_search.asp)
Good luck!

Find longest substring without repeating characters

Given a string S of length N find longest substring without repeating characters.
Example:
Input: "stackoverflow"
Output: "stackoverfl"
If there are two such candidates, return first from left. I need linear time and constant space algorithm.
You are going to need a start and an end locator(/pointer) for the
string and an array where you store information for each character:
did it occour at least once?
Start at the beginning of the string, both locators point to the
start of the string.
Move the end locator to the right till you find
a repetition (or reach the end of the string). For each processed character, store it in the array.
When stopped store the position if this is the largest substring. Also remember the repeated character.
Now do the same thing with the start locator, when processing
each character, remove its flags from the array. Move the locator till
you find the earlier occurrence of the repeated character.
Go back to step 3 if you haven't reached the end of string.
Overall: O(N)
import java.util.HashSet;
public class SubString {
public static String subString(String input){
HashSet<Character> set = new HashSet<Character>();
String longestOverAll = "";
String longestTillNow = "";
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (set.contains(c)) {
longestTillNow = "";
set.clear();
}
longestTillNow += c;
set.add(c);
if (longestTillNow.length() > longestOverAll.length()) {
longestOverAll = longestTillNow;
}
}
return longestOverAll;
}
public static void main(String[] args) {
String input = "substringfindout";
System.out.println(subString(input));
}
}
You keep an array indicating the position at which a certain character occurred last. For convenience all characters occurred at position -1. You iterate on the string keeping a window, if a character is repeated in that window, you chop off the prefix that ends with the first occurrence of this character. Throughout, you maintain the longest length. Here's a python implementation:
def longest_unique_substr(S):
# This should be replaced by an array (size = alphabet size).
last_occurrence = {}
longest_len_so_far = 0
longest_pos_so_far = 0
curr_starting_pos = 0
curr_length = 0
for k, c in enumerate(S):
l = last_occurrence.get(c, -1)
# If no repetition within window, no problems.
if l < curr_starting_pos:
curr_length += 1
else:
# Check if it is the longest so far
if curr_length > longest_len_so_far:
longest_pos_so_far = curr_starting_pos
longest_len_so_far = curr_length
# Cut the prefix that has repetition
curr_length -= l - curr_starting_pos
curr_starting_pos = l + 1
# In any case, update last_occurrence
last_occurrence[c] = k
# Maybe the longest substring is a suffix
if curr_length > longest_len_so_far:
longest_pos_so_far = curr_starting_pos
longest_len_so_far = curr_length
return S[longest_pos_so_far:longest_pos_so_far + longest_len_so_far]
EDITED:
following is an implementation of the concesus. It occured to me after my original publication. so as not to delete original, it is presented following:
public static String longestUniqueString(String S) {
int start = 0, end = 0, length = 0;
boolean bits[] = new boolean[256];
int x = 0, y = 0;
for (; x < S.length() && y < S.length() && length < S.length() - x; x++) {
bits[S.charAt(x)] = true;
for (y++; y < S.length() && !bits[S.charAt(y)]; y++) {
bits[S.charAt(y)] = true;
}
if (length < y - x) {
start = x;
end = y;
length = y - x;
}
while(y<S.length() && x<y && S.charAt(x) != S.charAt(y))
bits[S.charAt(x++)]=false;
}
return S.substring(start, end);
}//
ORIGINAL POST:
Here is my two cents. Test strings included. boolean bits[] = new boolean[256] may be larger to encompass some larger charset.
public static String longestUniqueString(String S) {
int start=0, end=0, length=0;
boolean bits[] = new boolean[256];
int x=0, y=0;
for(;x<S.length() && y<S.length() && length < S.length()-x;x++) {
Arrays.fill(bits, false);
bits[S.charAt(x)]=true;
for(y=x+1;y<S.length() && !bits[S.charAt(y)];y++) {
bits[S.charAt(y)]=true;
}
if(length<y-x) {
start=x;
end=y;
length=y-x;
}
}
return S.substring(start,end);
}//
public static void main(String... args) {
String input[][] = { { "" }, { "a" }, { "ab" }, { "aab" }, { "abb" },
{ "aabc" }, { "abbc" }, { "aabbccdefgbc" },
{ "abcdeafghicabcdefghijklmnop" },
{ "abcdeafghicabcdefghijklmnopqrabcdx" },
{ "zxxaabcdeafghicabcdefghijklmnopqrabcdx" },
{"aaabcdefgaaa"}};
for (String[] a : input) {
System.out.format("%s *** GIVES *** {%s}%n", Arrays.toString(a),
longestUniqueString(a[0]));
}
}
Here is one more solution with only 2 string variables:
public static String getLongestNonRepeatingString(String inputStr){
if(inputStr == null){
return null;
}
String maxStr = "";
String tempStr = "";
for(int i=0; i < inputStr.length(); i++){
// 1. if tempStr contains new character, then change tempStr
if(tempStr.contains("" + inputStr.charAt(i))){
tempStr = tempStr.substring(tempStr.lastIndexOf(inputStr.charAt(i)) + 1);
}
// 2. add new character
tempStr = tempStr + inputStr.charAt(i);
// 3. replace maxStr with tempStr if tempStr is longer
if(maxStr.length() < tempStr.length()){
maxStr = tempStr;
}
}
return maxStr;
}
Algorithm in JavaScript (w/ lots of comments)..
/**
Given a string S find longest substring without repeating characters.
Example:
Input: "stackoverflow"
Output: "stackoverfl"
Input: "stackoverflowabcdefghijklmn"
Output: "owabcdefghijklmn"
*/
function findLongestNonRepeatingSubStr(input) {
var chars = input.split('');
var currChar;
var str = "";
var longestStr = "";
var hash = {};
for (var i = 0; i < chars.length; i++) {
currChar = chars[i];
if (!hash[chars[i]]) { // if hash doesn't have the char,
str += currChar; //add it to str
hash[chars[i]] = {index:i};//store the index of the char
} else {// if a duplicate char found..
//store the current longest non-repeating chars. until now
//In case of equal-length, <= right-most str, < will result in left most str
if(longestStr.length <= str.length) {
longestStr = str;
}
//Get the previous duplicate char's index
var prevDupeIndex = hash[currChar].index;
//Find all the chars AFTER previous duplicate char and current one
var strFromPrevDupe = input.substring(prevDupeIndex + 1, i);
//*NEW* longest string will be chars AFTER prevDupe till current char
str = strFromPrevDupe + currChar;
//console.log(str);
//Also, Reset hash to letters AFTER duplicate letter till current char
hash = {};
for (var j = prevDupeIndex + 1; j <= i; j++) {
hash[input.charAt(j)] = {index:j};
}
}
}
return longestStr.length > str.length ? longestStr : str;
}
//console.log("stackoverflow => " + findLongestNonRepeatingSubStr("stackoverflow"));
//returns stackoverfl
//console.log("stackoverflowabcdefghijklmn => " +
findLongestNonRepeatingSubStr("stackoverflowabcdefghijklmn")); //returns owabcdefghijklmn
//console.log("1230123450101 => " + findLongestNonRepeatingSubStr("1230123450101")); //
returns 234501
We can consider all substrings one by one and check for each substring whether it contains all unique characters or not.
There will be n*(n+1)/2 substrings. Whether a substirng contains all unique characters or not can be checked in linear time by
scanning it from left to right and keeping a map of visited characters. Time complexity of this solution would be O(n^3).`
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class LengthOfLongestSubstringWithOutRepeatingChar {
public static void main(String[] args)
{
String s="stackoverflow";
//allSubString(s);
System.out.println("result of find"+find(s));
}
public static String find(String s)
{
List<String> allSubsring=allSubString(s);
Set<String> main =new LinkedHashSet<String>();
for(String temp:allSubsring)
{
boolean a = false;
for(int i=0;i<temp.length();i++)
{
for(int k=temp.length()-1;k>i;k--)
{
if(temp.charAt(k)==temp.charAt(i))
a=true;
}
}
if(!a)
{
main.add(temp);
}
}
/*for(String x:main)
{
System.out.println(x);
}*/
String res=null;
int min=0,max=s.length();
for(String temp:main)
{
if(temp.length()>min&&temp.length()<max)
{
min=temp.length();
res=temp;
}
}
System.out.println(min+"ha ha ha"+res+"he he he");
return res;
}
//substrings left to right ban rahi hai
private static List<String> allSubString(String str) {
List<String> all=new ArrayList<String>();
int c=0;
for (int i = 0; i < str.length(); i++) {
for (int j = 0; j <= i; j++) {
if (!all.contains(str.substring(j, i + 1)))
{
c++;
all.add(str.substring(j, i + 1));
}
}
}
for(String temp:all)
{
System.out.println("substring :-"+temp);
}
System.out.println("count"+c);
return all;
}
}
Another O(n) JavaScript solution. It does not alter strings during the looping; it just keeps track of the offset and length of the longest sub string so far:
function longest(str) {
var hash = {}, start, end, bestStart, best;
start = end = bestStart = best = 0;
while (end < str.length) {
while (hash[str[end]]) hash[str[start++]] = 0;
hash[str[end]] = 1;
if (++end - start > best) bestStart = start, best = end - start;
}
return str.substr(bestStart, best);
}
// I/O for snippet
document.querySelector('input').addEventListener('input', function () {
document.querySelector('span').textContent = longest(this.value);
});
Enter word:<input><br>
Longest: <span></span>
simple python snippet
l=length p=position
maxl=maxlength maxp=maxposition
Tested and working. For easy understanding, I suppose there's a drawer to put the letters.
Function:
public int lengthOfLongestSubstring(String s) {
int maxlen = 0;
int start = 0;
int end = 0;
HashSet<Character> drawer = new HashSet<Character>();
for (int i=0; i<s.length(); i++) {
char ch = s.charAt(i);
if (drawer.contains(ch)) {
//search for ch between start and end
while (s.charAt(start)!=ch) {
//drop letter from drawer
drawer.remove(s.charAt(start));
start++;
}
//Do not remove from drawer actual char (it's the new recently found)
start++;
end++;
}
else {
drawer.add(ch);
end++;
int _maxlen = end-start;
if (_maxlen>maxlen) {
maxlen=_maxlen;
}
}
}
return maxlen;
}
Longest substring without repeating character in python
public int lengthOfLongestSubstring(String s) {
if(s.equals(""))
return 0;
String[] arr = s.split("");
HashMap<String,Integer> map = new HashMap<>();
Queue<String> q = new LinkedList<>();
int l_till = 1;
int l_all = 1;
map.put(arr[0],0);
q.add(arr[0]);
for(int i = 1; i < s.length(); i++){
if (map.containsKey(arr[i])) {
if(l_till > l_all){
l_all = l_till;
}
while(!q.isEmpty() && !q.peek().equals(arr[i])){
map.remove(q.remove());
}
if(!q.isEmpty())
map.remove(q.remove());
q.add(arr[i]);
map.put(arr[i],i);
//System.out.println(q);
//System.out.println(map);
l_till = q.size();
}
else {
l_till = l_till + 1;
map.put(arr[i],i);
q.add(arr[i]);
}
}
if(l_till > l_all){
l_all = l_till;
}
return l_all;
}
I was asked the same question in an interview.
I have written Python3 code, to find the first occurrence of the substring with all distinct chars. In my implementations, I start with index = 0 and iterate over the input string. While iterating used a Python dict seems to store indexes of chars in input-string those has been visited in the iteration.
In iteration, if char c, does not find in current substring – raise KeyError exception
if c is found to be a duplicate char in the current substring (as c previously appeared during iteration – named that index last_seen) start a new substring
def lds(string: str) -> str:
""" returns first longest distinct substring in input `string` """
seens = {}
start, end, curt_start = 0, 0, 0
for curt_end, c in enumerate(string):
try:
last_seen = seens[c]
if last_seen < curt_start:
raise KeyError(f"{c!r} not found in {string[curt_start: curt_end]!r}")
if end - start < curt_end - curt_start:
start, end = curt_start, curt_end
curt_start = last_seen + 1
except KeyError:
pass
seens[c] = curt_end
else:
# case when the longest substring is suffix of the string, here curt_end
# do not point to a repeating char hance included in the substring
if string and end - start < curt_end - curt_start + 1:
start, end = curt_start, curt_end + 1
return string[start: end]
private static string LongestSubstring(string word)
{
var set = new HashSet<char>();
string longestOverAll = "";
string longestTillNow = "";
foreach (char c in word)
{
if (!set.Contains(c))
{
longestTillNow += c;
set.Add(c);
}
else
{
longestTillNow = string.Empty;
}
if (longestTillNow.Length > longestOverAll.Length)
{
longestOverAll = longestTillNow;
}
}
return longestOverAll;
}
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
public class LongestSubString2 {
public static void main(String[] args) {
String input = "stackoverflowabcdefghijklmn";
List<String> allOutPuts = new ArrayList<String>();
TreeMap<Integer, Set> map = new TreeMap<Integer, Set>();
for (int k = 0; k < input.length(); k++) {
String input1 = input.substring(k);
String longestSubString = getLongestSubString(input1);
allOutPuts.add(longestSubString);
}
for (String str : allOutPuts) {
int strLen = str.length();
if (map.containsKey(strLen)) {
Set set2 = (HashSet) map.get(strLen);
set2.add(str);
map.put(strLen, set2);
} else {
Set set1 = new HashSet();
set1.add(str);
map.put(strLen, set1);
}
}
System.out.println(map.lastKey());
System.out.println(map.get(map.lastKey()));
}
private static void printArray(Object[] currentObjArr) {
for (Object obj : currentObjArr) {
char str = (char) obj;
System.out.println(str);
}
}
private static String getLongestSubString(String input) {
Set<Character> set = new LinkedHashSet<Character>();
String longestString = "";
int len = input.length();
for (int i = 0; i < len; i++) {
char currentChar = input.charAt(i);
boolean isCharAdded = set.add(currentChar);
if (isCharAdded) {
if (i == len - 1) {
String currentStr = getStringFromSet(set);
if (currentStr.length() > longestString.length()) {
longestString = currentStr;
}
}
continue;
} else {
String currentStr = getStringFromSet(set);
if (currentStr.length() > longestString.length()) {
longestString = currentStr;
}
set = new LinkedHashSet<Character>(input.charAt(i));
}
}
return longestString;
}
private static String getStringFromSet(Set<Character> set) {
Object[] charArr = set.toArray();
StringBuffer strBuff = new StringBuffer();
for (Object obj : charArr) {
strBuff.append(obj);
}
return strBuff.toString();
}
}
This is my solution, and it was accepted by leetcode. However, after I saw the stats, I saw whole lot solutions has much faster result....meaning, my solution is around 600ms for all their test cases, and most of the js solutions are around 200 -300 ms bracket.. who can tell me why my solution is slowwww??
var lengthOfLongestSubstring = function(s) {
var arr = s.split("");
if (s.length === 0 || s.length === 1) {
return s.length;
}
var head = 0,
tail = 1;
var str = arr[head];
var maxL = 0;
while (tail < arr.length) {
if (str.indexOf(arr[tail]) == -1) {
str += arr[tail];
maxL = Math.max(maxL, str.length);
tail++;
} else {
maxL = Math.max(maxL, str.length);
head = head + str.indexOf(arr[tail]) + 1;
str = arr[head];
tail = head + 1;
}
}
return maxL;
};
I am posting O(n^2) in python . I just want to know whether the technique mentioned by Karoly Horvath has any steps that are similar to existing search/sort algorithms ?
My code :
def main():
test='stackoverflow'
tempstr=''
maxlen,index=0,0
indexsubstring=''
print 'Original string is =%s\n\n' %test
while(index!=len(test)):
for char in test[index:]:
if char not in tempstr:
tempstr+=char
if len(tempstr)> len(indexsubstring):
indexsubstring=tempstr
elif (len(tempstr)>=maxlen):
maxlen=len(tempstr)
indexsubstring=tempstr
break
tempstr=''
print 'max substring length till iteration with starting index =%s is %s'%(test[index],indexsubstring)
index+=1
if __name__=='__main__':
main()
Simple and Easy
import java.util.Scanner;
public class longestsub {
static Scanner sn = new Scanner(System.in);
static String word = sn.nextLine();
public static void main(String[] args) {
System.out.println("The Length is " +check(word));
}
private static int check(String word) {
String store="";
for (int i = 0; i < word.length(); i++) {
if (store.indexOf(word.charAt(i))<0) {
store = store+word.charAt(i);
}
}
System.out.println("Result word " +store);
return store.length();
}
}
Not quite optimized but simple answer in Python
def lengthOfLongestSubstring(s):
temp,maxlen,newstart = {},0,0
for i,x in enumerate(s):
if x in temp:
newstart = max(newstart,s[:i].rfind(x)+1)
else:
temp[x] = 1
maxlen = max(maxlen, len(s[newstart:i + 1]))
return maxlen
I think the costly affair is rfind which is why it's not quite optimized.
This is my solution. Hope it helps.
function longestSubstringWithoutDuplication(str) {
var max = 0;
//if empty string
if (str.length === 0){
return 0;
} else if (str.length === 1){ //case if the string's length is 1
return 1;
}
//loop over all the chars in the strings
var currentChar,
map = {},
counter = 0; //count the number of char in each substring without duplications
for (var i=0; i< str.length ; i++){
currentChar = str.charAt(i);
//if the current char is not in the map
if (map[currentChar] == undefined){
//push the currentChar to the map
map[currentChar] = i;
if (Object.keys(map).length > max){
max = Object.keys(map).length;
}
} else { //there is duplacation
//update the max
if (Object.keys(map).length > max){
max = Object.keys(map).length;
}
counter = 0; //initilize the counter to count next substring
i = map[currentChar]; //start from the duplicated char
map = {}; // clean the map
}
}
return max;
}
here is my javascript and cpp implementations with great details: https://algorithm.pingzhang.io/String/longest_substring_without_repeating_characters.html
We want to find the longest substring without repeating characters. The first thing comes to my mind is that we need a hash table to store every character in a substring so that when a new character comes in, we can easily know whether this character is already in the substring or not. I call it as valueIdxHash. Then, a substring has a startIdx and endIdx. So we need a variable to keep track of the starting index of a substring and I call it as startIdx. Let's assume we are at index i and we already have a substring (startIdx, i - 1). Now, we want to check whether this substring can keep growing or not.
If the valueIdxHash contains str[i], it means it is a repeated character. But we still need to check whether this repeated character is in the substring (startIdx, i - 1). So we need to retrieve the index of str[i] that is appeared last time and then compare this index with startIdx.
If startIdx is larger, it means the last appeared str[i] is outside of the substring. Thus the subtring can keep growing.
If startIdx is smaller, it means the last appeared str[i] is within of the substring. Thus, the substring cannot grow any more. startIdx will be updated as valueIdxHash[str[i]] + 1 and the new substring (valueIdxHash[str[i]] + 1, i) has potential to keep growing.
If the valueIdxHash does not contain str[i], the substring can keep growing.
I modified my solution to "find the length of the longest substring without repeating characters".
public string LengthOfLongestSubstring(string s) {
var res = 0;
var dict = new Dictionary<char, int>();
var start = 0;
for(int i =0; i< s.Length; i++)
{
if(dict.ContainsKey(s[i]))
{
start = Math.Max(start, dict[s[i]] + 1); //update start index
dict[s[i]] = i;
}
else
{
dict.Add(s[i], i);
}
res = Math.Max(res, i - start + 1); //track max length
}
return s.Substring(start,res);
}
import java.util.HashMap;
import java.util.HashSet;
public class SubString {
public static String subString(String input) {
String longesTillNOw = "";
String longestOverAll = "";
HashMap<Character,Integer> chars = new HashMap<>();
char[] array=input.toCharArray();
int start=0;
for (int i = 0; i < array.length; i++) {
char charactor = array[i];
if (chars.containsKey(charactor) ) {
start=chars.get(charactor)+1;
i=start;
chars.clear();
longesTillNOw = "";
} else {
chars.put(charactor,i);
longesTillNOw = longesTillNOw + charactor;
if (longesTillNOw.length() > longestOverAll.length()) {
longestOverAll = longesTillNOw;
}
}
}
return longestOverAll;
}
public static void main(String[] args) {
String input = "stackoverflowabcdefghijklmn";
System.out.println(subString(input));
}
}
Here are two ways to approach this problem in JavaScript.
A Brute Force approach is to loop through the string twice, checking every substring against every other substring and finding the maximum length where the substring is unique. We'll need two functions: one to check if a substring is unique and a second function to perform our double loop.
// O(n) time
const allUnique = str => {
const set = [...new Set(str)];
return (set.length == str.length) ? true: false;
}
// O(n^3) time, O(k) size where k is the size of the set
const lengthOfLongestSubstring = str => {
let result = 0,
maxResult = 0;
for (let i=0; i<str.length-1; i++) {
for (let j=i+1; j<str.length; j++) {
if (allUnique(str.substring(i, j))) {
result = str.substring(i, j).length;
if (result > maxResult) {
maxResult = result;
}
}
}
return maxResult;
}
}
This has a time complexity of O(n^3) since we perform a double loop O(n^2) and then another loop on top of that O(n) for our unique function. The space is the size of our set which can be generalized to O(n) or more accurately O(k) where k is the size of the set.
A Greedy Approach is to loop through only once and keep track of the maximum unique substring length as we go. We can use either an array or a hash map, but I think the new .includes() array method is cool, so let's use that.
const lengthOfLongestSubstring = str => {
let result = [],
maxResult = 0;
for (let i=0; i<str.length; i++) {
if (!result.includes(str[i])) {
result.push(str[i]);
} else {
maxResult = i;
}
}
return maxResult;
}
This has a time complexity of O(n) and a space complexity of O(1).
This problem can be solved in O(n) time complexity.
Initialize three variables
Start (index pointing to the start of the non repeating substring, Initialize it as 0 ).
End (index pointing to the end of the non repeating substring, Initialize it as 0 )
Hasmap (Object containing the last visited index positions of characters. Ex : {'a':0, 'b':1} for string "ab")
Steps :
Iterate over the string and perform following actions.
If the current character is not present in hashmap (), add it as to
hashmap, character as key and its index as value.
If current character is present in hashmap, then
a) Check whether the start index is less than or equal to the value present in the hashmap against the character (last index of same character earlier visited),
b) it is less then assign start variables value as the hashmaps' value + 1 (last index of same character earlier visited + 1);
c) Update hashmap by overriding the hashmap's current character's value as current index of character.
d) Calculate the end-start as the longest substring value and update if it's greater than earlier longest non-repeating substring.
Following is the Javascript Solution for this problem.
var lengthOfLongestSubstring = function(s) {
let length = s.length;
let ans = 0;
let start = 0,
end = 0;
let hashMap = {};
for (var i = 0; i < length; i++) {
if (!hashMap.hasOwnProperty(s[i])) {
hashMap[s[i]] = i;
} else {
if (start <= hashMap[s[i]]) {
start = hashMap[s[i]] + 1;
}
hashMap[s[i]] = i;
}
end++;
ans = ans > (end - start) ? ans : (end - start);
}
return ans;
};
Question: Find the longest substring without repeating characters.
Example 1 :
import java.util.LinkedHashMap;
import java.util.Map;
public class example1 {
public static void main(String[] args) {
String a = "abcabcbb";
// output => 3
System.out.println( lengthOfLongestSubstring(a));
}
private static int lengthOfLongestSubstring(String a) {
if(a == null || a.length() == 0) {return 0 ;}
int res = 0 ;
Map<Character , Integer> map = new LinkedHashMap<>();
for (int i = 0; i < a.length(); i++) {
char ch = a.charAt(i);
if (!map.containsKey(ch)) {
//If ch is not present in map, adding ch into map along with its position
map.put(ch, i);
}else {
/*
If char ch is present in Map, reposition the cursor i to the position of ch and clear the Map.
*/
i = map.put(ch, i);// updation of index
map.clear();
}//else
res = Math.max(res, map.size());
}
return res;
}
}
if you want the longest string without the repeating characters as output then do this inside the for loop:
String res ="";// global
int len = 0 ;//global
if(len < map.size()) {
len = map.size();
res = map.keySet().toString();
}
System.out.println("len -> " + len);
System.out.println("res => " + res);
def max_substring(string):
last_substring = ''
max_substring = ''
for x in string:
k = find_index(x,last_substring)
last_substring = last_substring[(k+1):]+x
if len(last_substring) > len(max_substring):
max_substring = last_substring
return max_substring
def find_index(x, lst):
k = 0
while k <len(lst):
if lst[k] == x:
return k
k +=1
return -1
can we use something like this .
def longestpalindrome(str1):
arr1=list(str1)
s=set(arr1)
arr2=list(s)
return len(arr2)
str1='abadef'
a=longestpalindrome(str1)
print(a)
if only length of the substring is to be returned
Algorithm: 1) Initialise an empty dictionary dct to check if any character already exists in the string. 2) cnt - to keep the count of substring without repeating characters. 3)l and r are the two pointers initialised to first index of the string. 4)loop through each char of the string. 5) If the character not present in the dct add itand increse the cnt. 6)If its already present then check if cnt is greater then resStrLen.7)Remove the char from dct and shift the left pointer by 1 and decrease the count.8)Repeat 5,6,7 till l,r greater or equal to length of the input string. 9)Have one more check at the end to handle cases like input string with non-repeating characters.Here is the simple python program to Find longest substring without repeating characters
a="stackoverflow"
strLength = len(a)
dct={}
resStrLen=0
cnt=0
l=0
r=0
strb=l
stre=l
while(l<strLength and r<strLength):
if a[l] in dct:
if cnt>resStrLen:
resStrLen=cnt
strb=r
stre=l
dct.pop(a[r])
cnt=cnt-1
r+=1
else:
cnt+=1
dct[a[l]]=1
l+=1
if cnt>resStrLen:
resStrLen=cnt
strb=r
stre=l
print "Result String Length : "+str(resStrLen)
print "Result String : " + a[strb:stre]
The solution in C.
#include<stdio.h>
#include <string.h>
void longstr(char* a, int *start, int *last)
{
*start = *last = 0;
int visited[256];
for (int i = 0; i < 256; i++)
{
visited[i] = -1;
}
int max_len = 0;
int cur_len = 0;
int prev_index;
visited[a[0]] = 0;
for (int i = 1; i < strlen(a); i++)
{
prev_index = visited[a[i]];
if (prev_index == -1 || i - cur_len > prev_index)
{
cur_len++;
*last = i;
}
else
{
if (max_len < cur_len)
{
*start = *last - cur_len;
max_len = cur_len;
}
cur_len = i - prev_index;
}
visited[a[i]] = i;
}
if (max_len < cur_len)
{
*start = *last - cur_len;
max_len = cur_len;
}
}
int main()
{
char str[] = "ABDEFGABEF";
printf("The input string is %s \n", str);
int start, last;
longstr(str, &start, &last);
//printf("\n %d %d \n", start, last);
memmove(str, (str + start), last - start);
str[last] = '\0';
printf("the longest non-repeating character substring is %s", str);
return 0;
}
public int lengthOfLongestSubstring(String s) {
int startIndex = 0;
int maxLength = 0;
//since we have 256 ascii chars
int[] lst = new int[256];
Arrays.fill(lst,-1);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//to get ascii value of c
int ic = (int) c;
int value = lst[ic];
//this will say to move start index to next index of the repeating char
//we only do this if the repeating char index is greater than start index
if (value >= startIndex) {
maxLength = Math.max(maxLength, i - startIndex);
startIndex = value + 1;
}
lst[ic] = i;
}
//when we came to an end of string
return Math.max(maxLength,s.length()-startIndex);
}
This is the fastest and it is linear time and constant space

Resources