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
This question already has answers here:
How to find whether the string is a Lapindrome? [closed]
(2 answers)
Closed 2 years ago.
The question is to check whether a given string is a lapindrome or not(CodeChef). According to the question, Lapindrome is defined as a string which when split in the middle, gives two halves having the same characters and same frequency of each character.
I have tried solving the problem using C++ with the code below
#include <iostream>
#include<cstring>
using namespace std;
bool lapindrome(char s[],int len){
int firstHalf=0,secondHalf=0;
char c;
for(int i=0,j=len-1;i<j;i++,j--){
firstHalf += int(s[i]);
secondHalf += int(s[j]);
}
if(firstHalf == secondHalf){
return true;
}
else
return false;
}
int main() {
// your code goes here
int t,len;
bool result;
char s[1000];
cin>>t;
while(t){
cin>>s;
len = strlen(s);
result = lapindrome(s,len);
if(result == true)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
--t;
}
return 0;
}
I have taken two count variables which will store the sum of ascii code of characters from first half and second half. Then those two variables are compared to check whether both the halves are equal or not.
I have tried the code on a couple of custom inputs and it works fine. But after I submit the code, the solution seems to be wrong.
Replace the lapindrome function to this one:
bool isLapindrome(std::string str)
{
int val1[MAX] = {0};
int val2[MAX] = {0};
int n = str.length();
if (n == 1)
return true;
for (int i = 0, j = n - 1; i < j; i++, j--)
{
val1[str[i] - 'a']++;
val2[str[j] - 'a']++;
}
for (int i = 0; i < MAX; i++)
if (val1[i] != val2[i])
return false;
return true;
}
Example Output
Input a string here: asdfsasd
The string is NOT a lapindrome.
---
Input a string here: asdfsdaf
The string is a lapindrome.
Enjoy!
You're not counting frequencies of the characters, only their sum. You could simply split the string into halves, create two maps for character frequencies of both sides e.g. std::map containing the count for each character. Then You can compare both maps with something like std::equal to check the complete equality of the maps (to see whether the halves are the same in terms of character frequency).
Instead of counting the frequency of characters (in the two halfs of input string) in two arrays or maps, it's actually sufficient to count them in one as well.
For this, negative counts have to be allowed.
Sample code:
#include <iostream>
#include <string>
#include <unordered_map>
bool isLapindrome(const std::string &text)
{
std::unordered_map<unsigned char, int> freq;
// iterate until index (growing from begin) and
// 2nd index (shrinking from end) cross over
for (size_t i = 0, j = text.size(); i < j--; ++i) {
++freq[(unsigned char)text[i]]; // count characters of 1st half positive
--freq[(unsigned char)text[j]]; // count characters of 2nd half negative
}
// check whether positive and negative counts didn't result in 0
// for at least one counted char
for (const std::pair<unsigned char, int> &entry : freq) {
if (entry.second != 0) return false;
}
// Otherwise, the frequencies were balanced.
return true;
}
int main()
{
auto check = [](const std::string &text) {
std::cout << '\'' << text << "': "
<< (isLapindrome(text) ? "yes" : "no")
<< '\n';
};
check("");
check("abaaab");
check("gaga");
check("abccab");
check("rotor");
check("xyzxy");
check("abbaab");
}
Output:
'': yes
'abaaab': yes
'gaga': yes
'abccab': yes
'rotor': yes
'xyzxy': yes
'abbaab': no
Live Demo on coliru
Note:
About the empty input string, I was a bit uncertain. If it's required to not to count as Lapindrome then an additional check is needed in isLapindrome(). This could be achieved with changing the final
return true;
to
return !text.empty(); // Empty input is considered as false.
The problem with your code was, that you only compare the sum of the characters. What's meant by frequency is that you have to count the occurrence of each character. Instead of counting frequencies in maps like in the other solutions here, you can simply sort and compare the two strings.
#include <iostream>
#include <string>
#include <algorithm>
bool lapindrome(const std::string& s) {
// true if size = 1, false if size = 0
if(s.size() <= 1) return (s.size());
std::string first_half = s.substr(0, s.size() / 2);
std::sort(first_half.begin(), first_half.end());
std::string second_half = s.substr(s.size() / 2 + s.size() % 2);
std::sort(second_half.begin(), second_half.end());
return first_half == second_half;
}
// here's a shorter hacky alternative:
bool lapindrome_short(std::string s) {
if (s.size() <= 1) return (s.size());
int half = s.size() / 2;
std::sort(s.begin(), s.begin() + half);
std::sort(s.rbegin(), s.rbegin() + half); // reverse half
return std::equal(s.begin(), s.begin() + half, s.rbegin());
}
int main() {
int count;
std::string input;
std::cin >> count;
while(count--) {
std::cin >> input;
std::cout << input << ": "
<< (lapindrome(input) ? "YES" : "NO") << std::endl;
}
return 0;
}
Live Demo
Given a string S.We need to tell if we can make it to palindrome by removing exactly one letter from it or not.
I have a O(N^2) approach by modifying Edit Distance method.Is their any better way ?
My Approach :
int ModifiedEditDistance(const string& a, const string& b, int k) {
int i, j, n = a.size();
int dp[MAX][MAX];
memset(dp, 0x3f, sizeof dp);
for (i = 0 ; i < n; i++)
dp[i][0] = dp[0][i] = i;
for (i = 1; i <= n; i++) {
int from = max(1, i-k), to = min(i+k, n);
for (j = from; j <= to; j++) {
if (a[i-1] == b[j-1]) // same character
dp[i][j] = dp[i-1][j-1];
// note that we don't allow letter substitutions
dp[i][j] = min(dp[i][j], 1 + dp[i][j-1]); // delete character j
dp[i][j] = min(dp[i][j], 1 + dp[i-1][j]); // insert character i
}
}
return dp[n][n];
}
How to improve space complexity as max size of string can go upto 10^5.
Please help.
Example : Let String be abc then answer is "NO" and if string is "abbcbba then answer is "YES"
The key observation is that if the first and last characters are the same then you needn't remove either of them; which is to say that xSTRINGx can be turned into a palindrome by removing a single letter if and only if STRING can (as long as STRING is at least one character long).
You want to define a method (excuse the Java syntax--I'm not a C++ coder):
boolean canMakePalindrome(String s, int startIndex, int endIndex, int toRemove);
which determines whether the part of the string from startIndex to endIndex-1 can be made into a palindrome by removing toRemove characters.
When you consider canMakePalindrome(s, i, j, r), then you can define it in terms of smaller problems like this:
If j-i is 1 then return true; if it's 0 then return true if and only if r is 0. The point here is that a 1-character string is a palindrome regardless of whether you remove a character; a 0-length string is a palindrome, but can't be made into one by removing a character (because there aren't any to remove).
If s[i] and s[j-1] are the same, then it's the same answer as canMakePalindrome(s, i+1, j-1, r).
If they're different, then either s[i] or s[j-1] needs removing. If toRemove is zero, then return false, because you haven't got any characters left to remove. If toRemove is 1, then return true if either canMakePalindrome(s, i+1, j, 0) or canMakePalindrome(s, i, j-1, 0). This is because you're now testing whether it's already a palindrome if you remove one of those two characters.
Now this can be coded up pretty easily, I think.
If you wanted to allow for removal of more than one character, you'd use the same idea, but using dynamic programming. With only one character to remove, dynamic programming will reduce the constant factor, but won't reduce the asymptotic time complexity (linear in the length of the string).
Psudocode (Something like this I havn't tested it at all).
It is based on detecting the conditions that you CAN remove a character, ie
There is exactly 1 wrong character
It is a palendrome (0 mismatch)
O(n) in time, O(1) in space.
bool foo(const std::string& s)
{
int i = 0;
int j = s.size()-1;
int mismatch_count = 0;
while (i < j)
{
if (s[i]==s[j])
{
i++; j--;
}
else
{
mismatch_count++;
if (mismatch_count > 1) break;
//override first preference if cannot find match for next character
if (s[i+1] == s[j] && ((i+2 >= j-1)||s[i+2]==s[j-1]))
{
i++;
}
else if (s[j-1]==s[i])
{
j--;
}
else
{
mismatch_count++; break;
}
}
}
//can only be a palendrome if you remove a character if there is exactly one mismatch
//or if a palendrome
return (mismatch_count == 1) || (mismatch_count == 0);
}
Here's a (slightly incomplete) solution which takes O(n) time and O(1) space.
// returns index to remove to make a palindrome; string::npos if not possible
size_t willYouBeMyPal(const string& str)
{
size_t toRemove = string::npos;
size_t len = str.length();
for (size_t c1 = 0, c2 = len - 1; c1 < c2; ++c1, --c2) {
if (str[c1] != str[c2]) {
if (toRemove != string::npos) {
return string::npos;
}
bool canRemove1 = str[c1 + 1] == str[c2];
bool canRemove2 = str[c1] == str[c2 - 1];
if (canRemove1 && canRemove2) {
abort(); // TODO: handle the case where both conditions are true
} else if (canRemove1) {
toRemove = c1++;
} else if (canRemove2) {
toRemove = c2--;
} else {
return string::npos;
}
}
}
// if str is a palindrome already, remove the middle char and it still is
if (toRemove == string::npos) {
toRemove = len / 2;
}
return toRemove;
}
Left as an exercise is what to do if you get this:
abxyxcxyba
The correct solution is:
ab_yxcxyba
But you might be led down a bad path:
abxyxcx_ba
So when you find the "next" character on both sides is a possible solution, you need to evaluate both possibilities.
I wrote a sample with O(n) complexity that works for the tests I threw at it. Not many though :D
The idea behind it is to ignore the first and last letters if they are the same, deleting one of them if they are not, and reasoning what happens when the string is small enough. The same result could be archived with a loop instead of the recursion, which would save some space (making it O(1)), but it's harder to understand and more error prone IMO.
bool palindrome_by_1(const string& word, int start, int end, bool removed = false) // Start includes, end excludes
{
if (end - start == 2){
if (!removed)
return true;
return word[start] == word[end - 1];
}
if (end - start == 1)
return true;
if (word[start] == word[end - 1])
return palindrome_by_1(word, start + 1, end - 1, removed);
// After this point we need to remove a letter
if (removed)
return false;
// When two letters don't match, try to eliminate one of them
return palindrome_by_1(word, start + 1, end, true) || palindrome_by_1(word, start, end - 1, true);
}
Checking if a single string is palindrome is O(n). You can implement a similar algorithm than moves two pointers, one from the start and another from the end. Move each pointer as long as the chars are the same, and on the first mismatch try to match which char you can skip, and keep moving both pointers as long as the rest chars are the same. Keep track of the first mismatch. This is O(n).
I hope my algorithm will pass without providing code.
If a word a1a2....an can be made a palindrome by removing ak, we can search for k as following:
If a1 != an, then the only possible k would be 1 or n. Just check if a1a2....an-1 or a2a3....an is a palindrome.
If a1 == an, next step is solving the same problem for a2....an-1. So we have a recursion here.
public static boolean pal(String s,int start,int end){
if(end-start==1||end==start)
return true;
if(s.charAt(start)==s.charAt(end))
return pal(s.substring(start+1, end),0,end-2);
else{
StringBuilder sb=new StringBuilder(s);
sb.deleteCharAt(start);
String x=new String(sb);
if(x.equals(sb.reverse().toString()))
return true;
StringBuilder sb2=new StringBuilder(s);
sb2.deleteCharAt(end);
String x2=new String(sb2);
if(x2.equals(sb2.reverse().toString()))
return true;
}
return false;
}
I tried the following,f and b are the indices at which characters do not match
int canwemakepal(char *str)//str input string
{
long int f,b,len,i,j;
int retval=0;
len=strlen(str);
f=0;b=len-1;
while(str[f]==str[b] && f<b)//continue matching till we dont get a mismatch
{
f++;b--;
}
if(f>=b)//if the index variable cross over each other, str is palindrome,answer is yes
{
retval=1;//true
}
else if(str[f+1]==str[b])//we get a mismatch,so check if removing character at str[f] will give us a palindrome
{
i=f+2;j=b-1;
while(str[i]==str[j] && i<j)
{
i++;j--;
}
if(i>=j)
retval=1;
else
retval=0;
}
else if(str[f]==str[b-1])//else check the same for str[b]
{
i=f+1;j=b-2;
while(str[i]==str[j] && i<j)
{
i++;j--;
}
if(i>=j)
retval=1;
else
retval=0;
}
else
retval=0;
return retval;
}
I created this solution,i tried with various input giving correct result,still not accepted as correct solution,Check it n let me know if m doing anything wrong!! Thanks in advance.
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
int t = s.nextInt();
String result[] = new String[t];
short i = 0;
while(i < t)
{
String str1 = s.next();
int length = str1.length();
String str2 = reverseString(str1);
if(str1.equals(str2))
{
result[i] = "Yes";
}
else
{
if(length == 2)
{
result[i] = "Yes";
}
else
{
int x = 0,y = length-1;
int counter = 0;
while(x<y)
{
if(str1.charAt(x) == str1.charAt(y))
{
x++;
y--;
}
else
{
counter ++;
if(str1.charAt(x) == str1.charAt(y-1))
{
y--;
}
else if(str1.charAt(x+1) == str1.charAt(y))
{
x++;
}
else
{
counter ++;
break;
}
}
}
if(counter >= 2)
{
result[i] = "No";
}
else
result[i]="Yes";
}
}
i++;
} // Loop over
for(int j=0; j<i;j++)
{
System.out.println(result[j]);
}
}
public static String reverseString(String original)
{
int length = original.length();
String reverse = "";
for ( int i = length - 1 ; i >= 0 ; i-- )
reverse = reverse + original.charAt(i);
return reverse;
}