How to find the longest substring containing two unique repeating characters - java

The task is to find the longest substring in a given string that is composed of any two unique repeating characters
Ex. in an input string "aabadefghaabbaagad", the longest such string is "aabbaa"
I came up with the following solution but wanted to see if there is a more efficient way to do the same
import java.util.*;
public class SubString {
public static void main(String[] args) {
//String inStr="defghgadaaaaabaababbbbbbd";
String inStr="aabadefghaabbaagad";
//String inStr="aaaaaaaaaaaaaaaaaaaa";
System.out.println("Input string is "+inStr);
StringBuilder sb = new StringBuilder(inStr.length());
String subStr="";
String interStr="";
String maxStr="";
int start=0,length=0, maxStart=0, maxlength=0, temp=0;
while(start+2<inStr.length())
{ int i=0;
temp=start;
char x = inStr.charAt(start);
char y = inStr.charAt(start+1);
sb.append(x);
sb.append(y);
while( (x==y) && (start+2<inStr.length()) )
{ start++;
y = inStr.charAt(start+1);
sb.append(y);
}
subStr=inStr.substring(start+2);
while(i<subStr.length())
{ if(subStr.charAt(i)==x || subStr.charAt(i)==y )
{ sb.append(subStr.charAt(i));
i++;
}
else
break;
}
interStr= sb.toString();
System.out.println("Intermediate string "+ interStr);
length=interStr.length();
if(maxlength<length)
{ maxlength=length;
length=0;
maxStr = new String(interStr);
maxStart=temp;
}
start++;
sb.setLength(0);
}
System.out.println("");
System.out.println("Longest string is "+maxStr.length()+" chars long "+maxStr);
}
}

Here's a hint that might guide you towards a linear-time algorithm (I assume that this is homework, so I won't give the entire solution): At the point where you have found a character that is neither equal to x nor to y, it is not necessary to go all the way back to start + 1 and restart the search. Let's take the string aabaaddaa. At the point where you have seen aabaa and the next character is d, there is no point in restarting the search at index 1 or 2, because in those cases, you'll only get abaa or baa before hitting d again. As a matter of fact, you can move start directly to index 3 (the first index of the last group of as), and since you already know that there is a contiguous sequene of as up to d, you can move i to index 5 and continue.
Edit: Pseudocode below.
// Find the first letter that is not equal to the first one,
// or return the entire string if it consists of one type of characters
int start = 0;
int i = 1;
while (i < str.length() && str[i] == str[start])
i++;
if (i == str.length())
return str;
// The main algorithm
char[2] chars = {str[start], str[i]};
int lastGroupStart = 0;
while (i < str.length()) {
if (str[i] == chars[0] || str[i] == chars[1]) {
if (str[i] != str[i - 1])
lastGroupStart = i;
}
else {
//TODO: str.substring(start, i) is a locally maximal string;
// compare it to the longest one so far
start = lastGroupStart;
lastGroupStart = i;
chars[0] = str[start];
chars[1] = str[lastGroupStart];
}
i++;
}
//TODO: After the loop, str.substring(start, str.length())
// is also a potential solution.

Same question to me, I wrote this code
public int getLargest(char [] s){
if(s.length<1) return s.length;
char c1 = s[0],c2=' ';
int start = 1,l=1, max=1;
int i = 1;
while(s[start]==c1){
l++;
start++;
if(start==s.length) return start;
}
c2 = s[start];
l++;
for(i = l; i<s.length;i++){
if(s[i]==c1 || s[i]==c2){
if(s[i]!=s[i-1])
start = i;
l++;
}
else {
l = i-start+1;
c1 = s[start];
c2 = s[i];
start = i;
}
max = Math.max(l, max);
}
return max;
}

so the way I think of this is to solve it in 2 steps
scan the entire string to find continuous streams of the same letter
loop the extracted segments and condense them until u get a gap.
This way you can also modify the logic to scan for longest sub-string of any length not just 2.
class Program
{
static void Main(string[] args)
{
//.
string input = "aabbccdddxxxxxxxxxxxxxxxxx";
int max_chars = 2;
//.
int flip = 0;
var scanned = new List<string>();
while (flip > -1)
{
scanned.Add(Scan(input, flip, ref flip));
}
string found = string.Empty;
for(int i=0;i<scanned.Count;i++)
{
var s = Condense(scanned, i, max_chars);
if (s.Length > found.Length)
{
found = s;
}
}
System.Console.WriteLine("Found:" + found);
System.Console.ReadLine();
}
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <param name="start"></param>
/// <returns></returns>
private static string Scan(string s, int start, ref int flip)
{
StringBuilder sb = new StringBuilder();
flip = -1;
sb.Append(s[start]);
for (int i = start+1; i < s.Length; i++)
{
if (s[i] == s[i - 1]) { sb.Append(s[i]); continue; } else { flip=i; break;}
}
return sb.ToString();
}
/// <summary>
///
/// </summary>
/// <param name="list"></param>
/// <param name="start"></param>
/// <param name="repeat"></param>
/// <param name="flip"></param>
/// <returns></returns>
private static string Condense(List<string> list, int start, int repeat)
{
StringBuilder sb = new StringBuilder();
List<char> domain = new List<char>(){list[start][0]};
for (int i = start; i < list.Count; i++)
{
bool gap = false;
for (int j = 0; j < domain.Count; j++)
{
if (list[i][0] == domain[j])
{
sb.Append(list[i]);
break;
}
else if (domain.Count < repeat)
{
domain.Add(list[i][0]);
sb.Append(list[i]);
break;
}
else
{
gap=true;
break;
}
}
if (gap) { break;}
}
return sb.ToString();
}
}

A general solution: Longest Substring Which Contains K Unique Characters.
int longestKCharSubstring(string s, int k) {
int i, max_len = 0, start = 0;
// either unique char & its last pos
unordered_map<char, int> ht;
for (i = 0; i < s.size(); i++) {
if (ht.size() < k || ht.find(s[i]) != ht.end()) {
ht[s[i]] = i;
} else {
// (k + 1)-th char
max_len = max(max_len, i - start);
// start points to the next of the earliest char
char earliest_char;
int earliest_char_pos = INT_MAX;
for (auto key : ht)
if (key.second < earliest_char_pos)
earliest_char = key.first;
start = ht[earliest_char] + 1;
// replace earliest_char
ht.erase(earliest_char);
ht[s[i]] = i;
}
}
// special case: e.g., "aaaa" or "aaabb" when k = 2
if (k == ht.size())
max_len = max(max_len, i - start);
return max_len;
}

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.Iterator; import java.util.List;
import java.util.Map;
public class PrintLLargestSubString {
public static void main(String[] args){ String string =
"abcdefghijklmnopqrstuvbcdefghijklmnopbcsdcelfabcdefghi";
List<Integer> list = new ArrayList<Integer> (); List<Integer>
keyList = new ArrayList<Integer> (); List<Integer> Indexlist = new
ArrayList<Integer> (); List<Integer> DifferenceList = new
ArrayList<Integer> (); Map<Integer, Integer> map = new
HashMap<Integer, Integer>(); int index = 0; int len = 1; int
j=1; Indexlist.add(0); for(int i = 0; i< string.length() ;i++) {
if(j< string.length()){
if(string.charAt(i) < string.charAt(j)){
len++;
list.add(len);
} else{
index= i+1;
Indexlist.add(index); // System.out.println("\nindex" + index);
len=1;
} } j++; } // System.out.println("\nlist" +list); System.out.println("index List" +Indexlist); // int n =
Collections.max(list); // int ind = Collections.max(Indexlist);
// System.out.println("Max number in IndexList " +n);
// System.out.println("Index Max is " +ind);
//Finding max difference in a list of elements for(int diff = 0;
diff< Indexlist.size()-1;diff++){ int difference =
Indexlist.get(diff+1)-Indexlist.get(diff);
map.put(Indexlist.get(diff), difference);
DifferenceList.add(difference); }
System.out.println("Difference between indexes" +DifferenceList); // Iterator<Integer> keySetIterator = map.keySet().iterator(); // while(keySetIterator.hasNext()){
// Integer key = keySetIterator.next();
// System.out.println("index: " + key + "\tDifference "
+map.get(key)); // // } // System.out.println("Diffferenece List" +DifferenceList); int maxdiff = Collections.max(DifferenceList); System.out.println("Max diff is " + maxdiff); ////// Integer
value = maxdiff; int key = 0; keyList.addAll(map.keySet());
Collections.sort(keyList); System.out.println("List of al keys"
+keyList); // System.out.println(map.entrySet()); for(Map.Entry entry: map.entrySet()){ if(value.equals(entry.getValue())){
key = (int) entry.getKey(); } } System.out.println("Key value of max difference starting element is " + key);
//Iterating key list and finding next key value int next = 0 ;
int KeyIndex = 0; int b; for(b= 0; b<keyList.size(); b++) {
if(keyList.get(b)==key){
KeyIndex = b; } } System.out.println("index of key\t" +KeyIndex); int nextIndex = KeyIndex+1; System.out.println("next Index = " +nextIndex); next = keyList.get(nextIndex);
System.out.println("next Index value is = " +next);
for( int z = KeyIndex; z < next ; z++) {
System.out.print(string.charAt(z)); } }
}

The problem can be solved in O(n). Idea is to maintain a window and add elements to the window till it contains less or equal 2, update our result if required while doing so. If unique elements exceeds than required in window, start removing the elements from left side.
#code
from collections import defaultdict
def solution(s, k):
length = len(set(list(s)))
count_dict = defaultdict(int)
if length < k:
return "-1"
res = []
final = []
maxi = -1
for i in range(0, len(s)):
res.append(s[i])
if len(set(res)) <= k:
if len(res) >= maxi and len(set(res)) <= k :
maxi = len(res)
final = res[:]
count_dict[maxi] += 1
else:
while len(set(res)) != k:
res = res[1:]
if maxi <= len(res) and len(set(res)) <= k:
maxi = len(res)
final = res[:]
count_dict[maxi] += 1
return len(final)
print(solution(s, k))

The idea here is to add occurrence of each character to a hashmap, and when the hasmap size increases more than k, remove the unwanted character.
private static int getMaxLength(String str, int k) {
if (str.length() == k)
return k;
var hm = new HashMap<Character, Integer>();
int maxLength = 0;
int startCounter = 0;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (hm.get(c) != null) {
hm.put(c, hm.get(c) + 1);
} else {
hm.put(c, 1);
}
//atmost K different characters
if (hm.size() > k) {
maxLength = Math.max(maxLength, i - startCounter);
while (hm.size() > k) {
char t = str.charAt(startCounter);
int count = hm.get(t);
if (count > 1) {
hm.put(t, count - 1);
} else {
hm.remove(t);
}
startCounter++;
}
}
}
return maxLength;
}

Related

Is there a test case scenario in which my program will fail?

Problem : You have L, a list containing some digits (0 to 9). Write a function solution(L) which finds the largest number that can be made from some or all of these digits and is divisible by 3. If it is not possible to make such a number, return 0 as the solution. L will contain anywhere from 1 to 9 digits. The same digit may appear multiple times in the list, but each element in the list may only be used once.
Test Cases :
Input:
Solution.solution({3, 1, 4, 1})
Output: 4311
Input:
Solution.solution({3, 1, 4, 1, 5, 9})
Output: 94311
My Program :
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.IntStream;
public class Solution {
static ArrayList<Integer> al = new ArrayList<Integer>();
static ArrayList<Integer> largest = new ArrayList<Integer>();
static int o = 1;
static int po = 0;
static void combinations(String[] digits, String[] data, int start, int end, int index, int r)
{
if (index == r)
{
String temp = "0";
for (int j = 0; j < r; j++)
{
temp = temp + data[j];
// System.out.print(data[j]);
}
Integer d = Integer.parseInt(temp);
al.add(d);
// System.out.println(al);
}
for (int i = start; i <= end && ((end - i + 1) >= (r - index)); i++)
{
data[index] = digits[i];
combinations(digits, data, i + 1, end, index + 1, r);
}
}
static void printCombinations(String[] sequence, int N)
{
String[] data = new String[N];
for (int r = 0; r < sequence.length; r++)
combinations(sequence, data, 0, N - 1, 0, r);
}
static String[] convert(int[] x)
{
String c[] = new String[x.length];
for(int i=0; i < x.length; i++)
{
Integer k = x[i];
if(k==0)
{
o = o * 10;
continue;
}
c[i] = k.toString();
}
// System.out.println(o);
c = Arrays.stream(c).filter(s -> (s != null && s.length() > 0)).toArray(String[]::new);
po = c.length;
// System.out.println("Come"+ Arrays.asList(c));
return c;
}
public static int solution(int[] l) {
if(l.length==0)
return 0;
if(IntStream.of(l).sum()%3==0)
{
String x = "";
Arrays.sort(l);
for (int i = l.length - 1; i >= 0; i--) {
x = x + l[i];
}
return Integer.parseInt(x);
}
printCombinations(convert(l),po);
al.sort(Comparator.reverseOrder());
al.remove(al.size()-1);
al.removeIf( num -> num%3!=0);
if(al.isEmpty())
return 0;
for(int i=0; i< al.size(); i++)
{
Integer n = al.get(i);
printMaxNum(n);
}
// System.out.println(al);
// System.out.println(largest);
return largest.get(0)*o;
}
static void printMaxNum(int num)
{
// hashed array to store count of digits
int count[] = new int[10];
// Converting given number to string
String str = Integer.toString(num);
// Updating the count array
for(int i=0; i < str.length(); i++)
count[str.charAt(i)-'0']++;
// result is to store the final number
int result = 0, multiplier = 1;
// Traversing the count array
// to calculate the maximum number
for (int i = 0; i <= 9; i++)
{
while (count[i] > 0)
{
result = result + (i * multiplier);
count[i]--;
multiplier = multiplier * 10;
}
}
// return the result
largest.add(result);
}
public static void main(String[] args) {
System.out.println(solution(new int[]{9,8,2,3}));
}
}
My Code passes both given test cases and 3 other hidden test cases except one. I tried all possible input combinations but couldn't get to the exact failure. The return type by default is given as int and therefore they would not pass values which give output that does not fit in int. Any other scenario where my code fails?

Even after using a global static array my values of the array are changing in java. How to overcome it?

In this code I am having some problem as I have marked using a loop which is printing some values. I am storing them in an array as mentioned and am trying to print the values in another function. But even after using the global array the value of the array is changing.
I am not able to figure out the problem. Please help me out.
import java.io.*;
import java.util.*;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
// Java program to print all permutations of a
// given string.
public class test3
{
static int[] val = new int[100] ; //array declaration as global
public static void main(String[] args)
{
System.out.println("An incremented value");
for(int i=2;i<=2;i++) {
String p="";
for(int j=0;j<=i;j++) {
for(int m=0;m<j;m++) {
p=p+"&";
}
for(int m=0;m<i-j;m++) {
p=p+"|";
}
printAllPermutations(p);
p="";
}
}
System.out.println();
for(int xy=0;xy<32;xy++)
System.out.print("["+xy+"]"+"="+val[xy]+" "); //trying to print the array
}
static void print(char[] temp) {
String a="";
System.out.println();
for (int i = 0; i < temp.length; i++)
{ System.out.print(temp[i]);
a=a+temp[i];}
System.out.print(" "+"opr:"+temp.length+" ");
final int N = temp.length+1;
/*===================CODE PROBLEM PART START=======================*/
for (int i = 0; i < (1 << N); i++) {
// System.out.println(zeroPad(Integer.toBinaryString(i), N));
String b=zeroPad(Integer.toBinaryString(i), N)+"";
// System.out.println("a: "+a+" b:"+b);
char[] arrayA = b.toCharArray();
char[] arrayB = a.toCharArray();
StringBuilder sb = new StringBuilder();
int ii = 0;
while( ii < arrayA.length && ii < arrayB.length){
sb.append(arrayA[ii]).append(arrayB[ii]);
++ii;
}
for(int j = ii; j < arrayA.length; ++j){
sb.append(arrayA[j]);
}
for(int j = ii; j < arrayB.length; ++j){
sb.append(arrayB[j]);
}
//System.out.println(sb.toString());
try {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine se = sem.getEngineByName("JavaScript");
String myExpression = sb.toString();
// System.out.print(se.eval(myExpression));
val[i]=(int)(se.eval(myExpression)); //inserting array value
System.out.print(val[i]); //NEED TO HAVE THESE VALUES IN THE 1-D ARRAY
// System.out.print(val[i]);
} catch (ScriptException e) {
System.out.println("Invalid Expression");
e.printStackTrace();}
}
/*===================CODE PROBLEM PART END========================*/
//
}
//unchangable = rest of the function
static int factorial(int n) {
int f = 1;
for (int i = 1; i <= n; i++)
f = f * i;
return f;
}
static int calculateTotal(char[] temp, int n) {
int f = factorial(n);
// Building HashMap to store frequencies of
// all characters.
HashMap<Character, Integer> hm =
new HashMap<Character, Integer>();
for (int i = 0; i < temp.length; i++) {
if (hm.containsKey(temp[i]))
hm.put(temp[i], hm.get(temp[i]) + 1);
else
hm.put(temp[i], 1);
}
// Traversing hashmap and finding duplicate elements.
for (Map.Entry e : hm.entrySet()) {
Integer x = (Integer)e.getValue();
if (x > 1) {
int temp5 = factorial(x);
f = f / temp5;
}
}
return f;
}
static void nextPermutation(char[] temp) {
// Start traversing from the end and
// find position 'i-1' of the first character
// which is greater than its successor.
int i;
for (i = temp.length - 1; i > 0; i--)
if (temp[i] > temp[i - 1])
break;
// Finding smallest character after 'i-1' and
// greater than temp[i-1]
int min = i;
int j, x = temp[i - 1];
for (j = i + 1; j < temp.length; j++)
if ((temp[j] < temp[min]) && (temp[j] > x))
min = j;
// Swapping the above found characters.
char temp_to_swap;
temp_to_swap = temp[i - 1];
temp[i - 1] = temp[min];
temp[min] = temp_to_swap;
// Sort all digits from position next to 'i-1'
// to end of the string.
Arrays.sort(temp, i, temp.length);
// Print the String
print(temp);
}
static void printAllPermutations(String s) {
// Sorting String
char temp[] = s.toCharArray();
Arrays.sort(temp);
// Print first permutation
print(temp);
// Finding the total permutations
int total = calculateTotal(temp, temp.length);
for (int i = 1; i < total; i++)
nextPermutation(temp);
}
static String zero(int L) {
return (L <= 0 ? "" : String.format("%0" + L + "d", 0));
}
static String zeroPad(String s, int L) {
return zero(L - s.length()) + s;
}
}
The output that I am getting is
An incremented value
|| opr:2 01111111 //WANT TO STORE THESE 32 VALUES IN 1 D ARRAY
&| opr:2 01010111 // AND PRINT THEM OUT
|& opr:2 00011111
&& opr:2 00000001
[0]=0 [1]=0 [2]=0 [3]=0 [4]=0 [5]=0 [6]=0 [7]=1 [8]=0 [9]=0 [10]=0 [11]=0 [12]=0 [13]=0 [14]=0 [15]=0 [16]=0 [17]=0 [18]=0 [19]=0 [20]=0 [21]=0 [22]=0 [23]=0 [24]=0 [25]=0 [26]=0 [27]=0 [28]=0 [29]=0 [30]=0 [31]=0
what I need to do is to store those 32 values in 1 D array for further operation but while doing it all the array values displays 0 only except [7]. I dont know whats going on here.
Reference types are not bound to local scopes, just because your array is static to the class it does not mean that changing the values in one function will not change the values in the actual array. The reference to your array as a parameter will be a copy, but the reference is still "pointing" on an actual object, which is not a copy bound to your local scope.
If you want to save two different states of the array, you will have to save them yourself.

Algorithm - Lexicographically largest possible magical substring

I am working on this magical sub-string problem.
Magical binary strings are non-empty binary strings if the following two conditions are true:
The number of 0's is equal to the number of 1's.
For every prefix of the binary string, the number of 1's should not be less than the number of 0's.
I got stuck on how to proceed further in my Java program.
Here is my program:
static String findLargest(String str) {
String[] splits = str.split("");
Set<String> set = new LinkedHashSet<String>();
for (int i = 0; i < splits.length; i++) {
if (splits[i].equals("0")) {
continue;
}
int zeros = 0;
int ones = 0;
StringBuilder sb = new StringBuilder("");
for (int j = i; j < splits.length; j++) {
if (splits[j].equals("0")) {
zeros++;
} else {
ones++;
}
sb.append(splits[j]);
if (zeros == ones && ones >= zeros) {
set.add(sb.toString());
}
}
}
set.remove(str);
List<String> list = new ArrayList<String>(set);
System.out.println(list);
return null;
}
Using this program I am able to get the magical sub-strings for the given input String 11011000 as [10, 101100, 1100] in my list variable.
Now from here I am struggling how to remove the invalid entry of 101100 from my list and then use the elements 10, 1100 to swap from my input 11011000 to get the final result as 11100100
Also please guide me if there is any other alternate approach.
If your question is about only eliminating the unwanted "101100" from the result, here is the answer
import java.util.ArrayList;
import java.util.HashMap;
import java.lang.*;
import java.util.Set;
import java.util.*;
public class HelloWorld{
public static void main(String []args){
findLargest("11011000");
}
public static String findLargest(String str) {
String[] splits = str.split("");
Set<String> set = new LinkedHashSet<String>();
for (int i = 0; i < splits.length; i++) {
if (splits[i].equals("0")) {
continue;
}
int zeros = 0;
int ones = 0;
StringBuilder sb = new StringBuilder("");
for (int j = i; j < splits.length; j++) {
if (splits[j].equals("0")) {
zeros++;
} else {
ones++;
}
sb.append(splits[j]);
if (zeros == ones && ones >= zeros) {
set.add(sb.toString());
j = i +1; // RESET THE INDEX ELEMENT TO SKIP THE SUBSTRING FROM CONSIDERATION
break; // BREAK FROM THE LOOP
}
}
}
set.remove(str);
List<String> list = new ArrayList<String>(set);
System.out.println(list);
return null;
}
}
I can provide some points.
First, get all the magical substrings and store them as a pair of start and end index(l, r) in a list;
Second, sort the list based on index l;
Those who can be potentially swapped substring can get from the same index l. look at the example given "11011000"
the list will have (0,7),(1,2),(1,6),(3,6),(4,5)
obviously only potential swap is among (1,2)(1,6)
deal these substrings have same index l will help find potential swapping substrings, sort them to find the maximum order.
class Pair{
int start;
int end;
}
public List<Pair> findmagicalPairs(String binString){
List<Pair> magicPairs = new ArrayList<Pair>();
for(int start=0;start<binString.length()-1;start++){
int ones=0;
int zeros=0;
for(int i=start; i<binString.length();i++){
if(binString.charAt(i) == '1'){
ones++;
} else if(binString.charAt(i)=='0'){
zeros++;
}
if(ones == zeros){ //check if magical
Pair temp=new Pair();
temp.start=start;
temp.end =i;
magicPairs.add(temp);
}
}
}
return magicPairs;
}
public String largestMagical(String binString) {
// Write your code here
List<Pair> allPairs = findmagicalPairs(binString);
String largest=binString;
//check by swapping each pairs
for(int i=0;i<allPairs.size()-1;i++){
for(int j=i+1;j<allPairs.size()-1;j++){
if(allPairs.get(i).end+1 == allPairs.get(j).start){
//consecutive Pair so swap and see largest
int index = allPairs.get(j).start;
String swapped = binString.substring(0,allPairs.get(i).start)+binString.substring(allPairs.get(j).start,allPairs.get(j).end+1)+binString.substring(allPairs.get(i).start,allPairs.get(i).end+1)+binString.substring(allPairs.get(j).end+1);
largest = LargestString(largest, swapped);
} else {
//else ignore
}
}
}
return largest;
}
public String LargestString(String first, String second){
if(first.compareTo(second)>0){
return first;
} else {
return second;
}
}
JavaScript Code!
const largestMagical = (binString) => {
//console.log({ binString });
const len = binString.length;
const height = Array(len + 1).fill(0),
num = { 1: 1, 0: -1 },
marked = Array(len + 1).fill(false),
sameHeights = {};
let i,
j,
result = "";
for (i = 1; i <= len; ++i) {
height[i] = height[i - 1] + num[binString[i - 1]];
}
//console.log({ height });
for (i = 0; i <= len; ++i) {
if (marked[i]) continue;
marked[i] = true;
sameHeights[i] = [i];
for (j = i + 1; j <= len; ++j) {
if (height[j] < height[i]) break;
if (height[j] === height[i]) {
sameHeights[i].push(j);
marked[j] = true;
}
}
}
//console.log({ sameHeights });
for (let k in sameHeights) {
const leng = sameHeights[k].length;
let startId, midId, endId;
for (startId = 0; startId < leng - 2; ++startId) {
for (midId = startId + 1; midId < leng - 1; ++midId) {
for (endId = midId + 1; endId < leng; ++endId) {
const start = sameHeights[k][startId],
mid = sameHeights[k][midId],
end = sameHeights[k][endId];
//console.log({start, mid, end});
const swapped =
binString.substring(0, start) +
binString.substring(mid, end) +
binString.substring(start, mid) +
binString.substring(end, len);
//console.log({swapped});
if (swapped > result) result = swapped;
}
}
}
}
return result;
};
console.log(largestMagical("1010111000"));
console.log(largestMagical("11011000"));

Optimising algorithm for an operation inserting program

The program I have currently takes N numbers and then a goal target. It inserts either "+" or "*" in between the numbers to try reach the goal. If it can reach the goal it will print out the correct operations.
However the way it finds the answer is by brute force, which is inadequate for a large set of N numbers. My current code is below:
public class Arithmetic4{
private static ArrayList<String> input = new ArrayList<String>();
private static ArrayList<String> second_line = new ArrayList<String>();
private static ArrayList<Integer> numbers = new ArrayList<Integer>();
private static ArrayList<String> operations = new ArrayList<String>();
private static ArrayList<Integer> temp_array = new ArrayList<Integer>();
public static void main(String [] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()){
readInput(sc);
}
}
public static void readInput(Scanner sc){
String line = sc.nextLine();
input.add(line);
line = sc.nextLine();
second_line.add(line);
dealInput();
}
public static void dealInput(){
String numberS = input.get(0);
String[] stringNumbers = numberS.split("\\s+");
for(int i = 0; i < stringNumbers.length; i++){
String numberAsString = stringNumbers[i];
numbers.add(Integer.parseInt(numberAsString));
}
String orderString = second_line.get(0);
String[] stringWhatWay = orderString.split("\\s+");
int target = Integer.parseInt(stringWhatWay[0]);
char whatway = stringWhatWay[1].charAt(0);
long startTime = System.currentTimeMillis();
whatEquation(numbers, target, whatway);
long elapsedTime = System.currentTimeMillis() - startTime;
long elapsedMSeconds = elapsedTime / 1;
System.out.println(elapsedMSeconds);
numbers.clear();
input.clear();
second_line.clear();
}
public static void whatEquation(ArrayList<Integer> numbers, int target, char whatway){
if(whatway != 'L' && whatway != 'N'){
System.out.println("Not an option");
}
if(whatway == 'N'){
ArrayList<Integer> tempo_array = new ArrayList<Integer>(numbers);
int count = 0;
for (int y: numbers) {
count++;
}
count--;
int q = count;
calculateN(numbers, target, tempo_array, q);
}
if (whatway == 'L'){
if(numbers.size() == 1){
System.out.println("L " + numbers.get(0));
}
ArrayList<Integer> temp_array = new ArrayList<Integer>(numbers);
calculateL(numbers, target, temp_array);
}
}
public static void calculateN(ArrayList<Integer> numbers, int target, ArrayList<Integer> tempo_numbers, int q){
int sum = 0;
int value_inc = 0;
int value_add;
boolean firstRun = true;
ArrayList<Character> ops = new ArrayList<Character>();
ops.add('+');
ops.add('*');
for(int i = 0; i < Math.pow(2, q); i++){
String bin = Integer.toBinaryString(i);
while(bin.length() < q)
bin = "0" + bin;
char[] chars = bin.toCharArray();
List<Character> oList = new ArrayList<Character> ();
for(char c: chars){
oList.add(c);
}
ArrayList<Character> op_array = new ArrayList<Character>();
ArrayList<Character> temp_op_array = new ArrayList<Character>();
for (int j = 0; j < oList.size(); j++) {
if (oList.get(j) == '0') {
op_array.add(j, ops.get(0));
temp_op_array.add(j, ops.get(0));
} else if (oList.get(j) == '1') {
op_array.add(j, ops.get(1));
temp_op_array.add(j, ops.get(1));
}
}
sum = 0;
for(int p = 0; p < op_array.size(); p++){
if(op_array.get(p) == '*'){
int multiSum = numbers.get(p) * numbers.get(p+1);
numbers.remove(p);
numbers.remove(p);
numbers.add(p, multiSum);
op_array.remove(p);
p -= 1;
}
}
for(Integer n: numbers){
sum += n;
}
if(sum != target){
numbers.clear();
for (int t = 0; t < tempo_numbers.size(); t++) {
numbers.add(t, tempo_numbers.get(t));
}
}
if (sum == target){
int count_print_symbol = 0;
System.out.print("N ");
for(int g = 0; g < tempo_numbers.size(); g++){
System.out.print(tempo_numbers.get(g) + " ");
if(count_print_symbol == q){
break;
}
System.out.print(temp_op_array.get(count_print_symbol) + " ");
count_print_symbol++;
}
System.out.print("\n");
return;
}
}
System.out.println("N is Impossible");
}
public static void calculateL(ArrayList<Integer> numbers, int target, ArrayList<Integer> temp_array){
int op_count = 0;
int sum = 0;
int n = (numbers.size() -1);
boolean firstRun = true;
for (int i = 0; i < Math.pow(2, n); i++) {
String bin = Integer.toBinaryString(i);
while (bin.length() < n)
bin = "0" + bin;
char[] chars = bin.toCharArray();
char[] charArray = new char[n];
for (int j = 0; j < chars.length; j++) {
charArray[j] = chars[j] == '0' ? '+' : '*';
}
//System.out.println(charArray);
for(char c : charArray){
op_count++;
if(firstRun == true){
sum = numbers.get(0);
numbers.remove(0);
// System.out.println(sum);
}
if (!numbers.isEmpty()){
if (c == '+') {
sum += numbers.get(0);
} else if (c == '*') {
sum *= numbers.get(0);
}
numbers.remove(0);
}
firstRun = false;
//System.out.println(sum);
if(sum == target && op_count == n){
int count_print_op = 0;
System.out.print("L ");
for(int r = 0; r < temp_array.size(); r++){
System.out.print(temp_array.get(r) + " ");
if(count_print_op == n){
break;
}
System.out.print(charArray[count_print_op] + " ");
count_print_op++;
}
System.out.print("\n");
return;
}
if(op_count == n && sum != target){
firstRun = true;
sum = 0;
op_count = 0;
for(int e = 0; e < temp_array.size(); e++){
numbers.add(e, temp_array.get(e));
}
}
}
}
System.out.println("L is impossible");
}
}
Is there a faster to way to reach a similar conclusion?
This problem can be solved in O(NKĀ²) using the Dynamic Programming paradigm, where K is the maximum possible value for the goal target. This is not that good and maybe there is a faster algorithm, but it's still a lot better than the O(2^N) brute force solution.
First let's define a recurrence to solve the problem: let G be the goal value and f(i,j,k) be a function that returns:
1 if we can reach the value G-j-k using only elements from index i and onwards
0 otherwise
We are going to use j as an accumulator that holds the current total sum and k as an accumulator that holds the total product of the current chain of multiplications, you will understand it soon.
The base cases for the recurrence are:
f(N,x,y) = 1 if x+y = G (we have used every element and reached our goal)
f(N,x,y) = 0 otherwise
f(i,x,y) = 0 i != N and x+y >= G (we have exceeded the goal before using every element)
For other i values we can define the recurrence as:
f(i,j,k) = max( f(i+1,j+k,v[i]) , f(i+1,j,k*v[i]) )
The first function call inside max() means that we will put a "+" sign before the current index, so our current multiplication chain is broken and we have to add its total product to the current sum, so the second parameter is j+k, and since we are starting a new multiplication chain right now, it's total product is exactly v[i].
The second function call inside max() means that we will put a "*" sign before the current index, so our current multiplication chain is still going on, so the second parameter remains j, and the third parameter will become k * v[i].
What we want is the value of f(0,0,0) (we haven't used any elements, and our current accumulated sums are equal to 0). f(0,0,0) equals 1 if and only if there is a solution for the problem, so the problem is solved. Now let's go back to the recurrence and fix a detail: when we run f(0,0,0), the value of k*v[i] will be 0 no matter the value of v[i], so we have to add a special check when we are computing the answer for i = 0, and the final recurrence will look like this:
f(i,j,k) = max( f(i+1,j+k,v[i]) , f(i+1,j,(i==0?v[i]:k*v[i])) )
Finally, we apply the memoization/dynamic programming paradigm to optimize the calculation of the recurrence. During the execution of the algorithm, we will keep track of every calculated state so when this state is called again by another recursive call we just return the stored value instead of computing its whole recursion tree again. Don't forget to do this or your solution is going to be as slow as a brute force solution (or even worse) due to recalculation of subproblems. If you need some resources on DP, you can start here: https://en.wikipedia.org/wiki/Dynamic_programming

How to find the longest substring with equal amount of characters efficiently

I have a string that consists of characters A,B,C and D and I am trying to calculate the length of the longest substring that has an equal amount of each one of these characters in any order.
For example ABCDB would return 4, ABCC 0 and ADDBCCBA 8.
My code currently:
public int longestSubstring(String word) {
HashMap<Integer, String> map = new HashMap<Integer, String>();
for (int i = 0; i<word.length()-3; i++) {
map.put(i, word.substring(i, i+4));
}
StringBuilder sb;
int longest = 0;
for (int i = 0; i<map.size(); i++) {
sb = new StringBuilder();
sb.append(map.get(i));
int a = 4;
while (i<map.size()-a) {
sb.append(map.get(i+a));
a+= 4;
}
String substring = sb.toString();
if (equalAmountOfCharacters(substring)) {
int length = substring.length();
if (length > longest)
longest = length;
}
}
return longest;
}
This currently works pretty well if the string length is 10^4 but I'm trying to make it 10^5. Any tips or suggestions would be appreciated.
Let's assume that cnt(c, i) is the number of occurrences of the character c in the prefix of length i.
A substring (low, high] has an equal amount of two characters a and b iff cnt(a, high) - cnt(a, low) = cnt(b, high) - cnt(b, low), or, put it another way, cnt(b, high) - cnt(a, high) = cnt(b, low) - cnt(a, low). Thus, each position is described by a value of cnt(b, i) - cnt(a, i). Now we can generalize it for more that two characters: each position is described by a tuple (cnt(a_2, i) - cnt(a_1, i), ..., cnt(a_k, i) - cnt(a_1, i)), where a_1 ... a_k is the alphabet.
We can iterate over the given string and maintain the current tuple. At each step, we should update the answer by checking the value of i - first_occurrence(current_tuple), where first_occurrence is a hash table that stores the first occurrence of each tuple seen so far. Do not forget to put a tuple of zeros to the hash map before iteration(it corresponds to an empty prefix).
If there were only A's and B's, then you could do something like this.
def longest_balanced(word):
length = 0
cumulative_difference = 0
first_index = {0: -1}
for index, letter in enumerate(word):
if letter == 'A':
cumulative_difference += 1
elif letter == 'B':
cumulative_difference -= 1
else:
raise ValueError(letter)
if cumulative_difference in first_index:
length = max(length, index - first_index[cumulative_difference])
else:
first_index[cumulative_difference] = index
return length
Life is more complicated with all four letters, but the idea is much the same. Instead of keeping just one cumulative difference, for A's versus B's, we keep three, for A's versus B's, A's versus C's, and A's versus D's.
Well, first of all abstain from constructing any strings.
If you don't produce any (or nearly no) garbage, there's no need to collect it, which is a major plus.
Next, use a different data-structure:
I suggest 4 byte-arrays, storing the count of their respective symbol in the 4-span starting at the corresponding string-index.
That should speed it up considerably.
You can count the occurrences of the characters in word. Then, a possible solution could be:
If min is the minimum number of occurrences of any character in word, then min is also the maximum possible number of occurrences of each character in the substring we are looking for. In the code below, min is maxCount.
We iterate over decreasing values of maxCount. At every step, the string we are searching for will have length maxCount * alphabetSize. We can view this as the size of a sliding window we can slide over word.
We slide the window over word, counting the occurrences of the characters in the window. If the window is the substring we are searching for, we return the result. Otherwise, we keep searching.
[FIXED] The code:
private static final int ALPHABET_SIZE = 4;
public int longestSubstring(String word) {
// count
int[] count = new int[ALPHABET_SIZE];
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
count[c - 'A']++;
}
int maxCount = word.length();
for (int i = 0; i < count.length; i++) {
int cnt = count[i];
if (cnt < maxCount) {
maxCount = cnt;
}
}
// iterate over maxCount until found
boolean found = false;
while (maxCount > 0 && !found) {
int substringLength = maxCount * ALPHABET_SIZE;
found = findSubstring(substringLength, word, maxCount);
if (!found) {
maxCount--;
}
}
return found ? maxCount * ALPHABET_SIZE : 0;
}
private boolean findSubstring(int length, String word, int maxCount) {
int startIndex = 0;
boolean found = false;
while (startIndex + length <= word.length()) {
int[] count = new int[ALPHABET_SIZE];
for (int i = startIndex; i < startIndex + length; i++) {
char c = word.charAt(i);
int cnt = ++count[c - 'A'];
if (cnt > maxCount) {
break;
}
}
if (equalValues(count, maxCount)) {
found = true;
break;
} else {
startIndex++;
}
}
return found;
}
// Returns true if all values in c are equal to value
private boolean equalValues(int[] count, int value) {
boolean result = true;
for (int i : count) {
if (i != value) {
result = false;
break;
}
}
return result;
}
[MERGED] This is Hollis Waite's solution using cumulative counts, but taking my observations at points 1. and 2. into consideration. This may improve performance for some inputs:
private static final int ALPHABET_SIZE = 4;
public int longestSubstring(String word) {
// count
int[][] cumulativeCount = new int[ALPHABET_SIZE][];
for (int i = 0; i < ALPHABET_SIZE; i++) {
cumulativeCount[i] = new int[word.length() + 1];
}
int[] count = new int[ALPHABET_SIZE];
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
count[c - 'A']++;
for (int j = 0; j < ALPHABET_SIZE; j++) {
cumulativeCount[j][i + 1] = count[j];
}
}
int maxCount = word.length();
for (int i = 0; i < count.length; i++) {
int cnt = count[i];
if (cnt < maxCount) {
maxCount = cnt;
}
}
// iterate over maxCount until found
boolean found = false;
while (maxCount > 0 && !found) {
int substringLength = maxCount * ALPHABET_SIZE;
found = findSubstring(substringLength, word, maxCount, cumulativeCount);
if (!found) {
maxCount--;
}
}
return found ? maxCount * ALPHABET_SIZE : 0;
}
private boolean findSubstring(int length, String word, int maxCount, int[][] cumulativeCount) {
int startIndex = 0;
int endIndex = (startIndex + length) - 1;
boolean found = true;
while (endIndex < word.length()) {
for (int i = 0; i < ALPHABET_SIZE; i++) {
if (cumulativeCount[i][endIndex] - cumulativeCount[i][startIndex] != maxCount) {
found = false;
break;
}
}
if (found) {
break;
} else {
startIndex++;
endIndex++;
}
}
return found;
}
You'll probably want to cache cumulative counts of characters for each index of String -- that's where the real bottleneck is. Haven't thoroughly tested but something like the below should work.
public class Test {
static final int LEN = 4;
static class RandomCharSequence implements CharSequence {
private final Random mRandom = new Random();
private final int mAlphabetLen;
private final int mLen;
private final int mOffset;
RandomCharSequence(int pLen, int pOffset, int pAlphabetLen) {
mAlphabetLen = pAlphabetLen;
mLen = pLen;
mOffset = pOffset;
}
public int length() {return mLen;}
public char charAt(int pIdx) {
mRandom.setSeed(mOffset + pIdx);
return (char) (
'A' +
(mRandom.nextInt() % mAlphabetLen + mAlphabetLen) % mAlphabetLen
);
}
public CharSequence subSequence(int pStart, int pEnd) {
return new RandomCharSequence(pEnd - pStart, pStart, mAlphabetLen);
}
#Override public String toString() {
return (new StringBuilder(this)).toString();
}
}
public static void main(String[] pArgs) {
Stream.of("ABCDB", "ABCC", "ADDBCCBA", "DADDBCCBA").forEach(
pWord -> System.out.println(longestSubstring(pWord))
);
for (int i = 0; ; i++) {
final double len = Math.pow(10, i);
if (len >= Integer.MAX_VALUE) break;
System.out.println("Str len 10^" + i);
for (int alphabetLen = 1; alphabetLen <= LEN; alphabetLen++) {
final Instant start = Instant.now();
final int val = longestSubstring(
new RandomCharSequence((int) len, 0, alphabetLen)
);
System.out.println(
String.format(
" alphabet len %d; result %08d; time %s",
alphabetLen,
val,
formatMillis(ChronoUnit.MILLIS.between(start, Instant.now()))
)
);
}
}
}
static String formatMillis(long millis) {
return String.format(
"%d:%02d:%02d.%03d",
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)),
TimeUnit.MILLISECONDS.toMillis(millis) -
TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(millis))
);
}
static int longestSubstring(CharSequence pWord) {
// create array that stores cumulative char counts at each index of string
// idx 0 = char (A-D); idx 1 = offset
final int[][] cumulativeCnts = new int[LEN][];
for (int i = 0; i < LEN; i++) {
cumulativeCnts[i] = new int[pWord.length() + 1];
}
final int[] cumulativeCnt = new int[LEN];
for (int i = 0; i < pWord.length(); i++) {
cumulativeCnt[pWord.charAt(i) - 'A']++;
for (int j = 0; j < LEN; j++) {
cumulativeCnts[j][i + 1] = cumulativeCnt[j];
}
}
final int maxResult = Arrays.stream(cumulativeCnt).min().orElse(0) * LEN;
if (maxResult == 0) return 0;
int result = 0;
for (int initialOffset = 0; initialOffset < LEN; initialOffset++) {
for (
int start = initialOffset;
start < pWord.length() - result;
start += LEN
) {
endLoop:
for (
int end = start + result + LEN;
end <= pWord.length() && end - start <= maxResult;
end += LEN
) {
final int substrLen = end - start;
final int expectedCharCnt = substrLen / LEN;
for (int i = 0; i < LEN; i++) {
if (
cumulativeCnts[i][end] - cumulativeCnts[i][start] !=
expectedCharCnt
) {
continue endLoop;
}
}
if (substrLen > result) result = substrLen;
}
}
}
return result;
}
}
Suppose there are K possible letters in a string of length N. We could track the balance of letters seen with a vector pos of length K that is updated as follows:
If letter 1 is seen, add (K-1, -1, -1, ...)
If letter 2 is seen, add (-1, K-1, -1, ...)
If letter 3 is seen, add (-1, -1, K-1, ...)
Maintain a hash that maps pos to the first string position where pos is reached. Balanced substrings occur whenever hash[pos] already exists and the substring value is s[hash[pos]:pos].
The cost of maintaining the hash is O(log N) so processing the string takes O(N log N). How does this compare with solutions so far? These types of problems tend to have linear solutions but I haven't come across one yet.
Here's some code demonstrating the idea for 3 letters and a run using biased random strings. (Uniform random strings allow for solutions that are around half the string length, which is unwieldy to print).
#!/usr/bin/python
import random
from time import time
alphabet = "abc"
DIM = len(alphabet)
def random_string(n):
# return a random string over choices[] of length n
# distribution of letters is non-uniform to make matches harder to find
choices = "aabbc"
s = ''
for i in range(n):
r = random.randint(0, len(choices) - 1)
s += choices[r]
return s
def validate(s):
# verify frequencies of each letter are the same
f = [0, 0, 0]
a2f = {alphabet[i] : i for i in range(DIM)}
for c in s:
f[a2f[c]] += 1
assert f[0] == f[1] and f[1] == f[2]
def longest_balanced(s):
"""return length of longest substring of s containing equal
populations of each letter in alphabet"""
slen = len(s)
p = [0 for i in range(DIM)]
vec = {alphabet[0] : [2, -1, -1],
alphabet[1] : [-1, 2, -1],
alphabet[2] : [-1, -1, 2]}
x = -1
best = -1
hist = {str([0, 0, 0]) : -1}
for c in s:
x += 1
p = [p[i] + vec[c][i] for i in range(DIM)]
pkey = str(p)
if pkey not in hist:
hist[pkey] = x
else:
span = x - hist[pkey]
assert span % DIM == 0
if span > best:
best = span
cand = s[hist[pkey] + 1: x + 1]
print("best so far %d = [%d,%d]: %s" % (best,
hist[pkey] + 1,
x + 1,
cand))
validate(cand)
return best if best > -1 else 0
def main():
#print longest_balanced( "aaabcabcbbcc" )
t0 = time()
s = random_string(1000000)
print "generate time:", time() - t0
t1 = time()
best = longest_balanced( s )
print "best:", best
print "elapsed:", time() - t1
main()
Sample run on an input of 10^6 letters with an alphabet of 3 letters:
$ ./bal.py
...
best so far 189 = [847894,848083]: aacacbcbabbbcabaabbbaabbbaaaacbcaaaccccbcbcbababaabbccccbbabbacabbbbbcaacacccbbaacbabcbccaabaccabbbbbababbacbaaaacabcbabcbccbabbccaccaabbcabaabccccaacccccbaacaaaccbbcbcabcbcacaabccbacccacca
best: 189
elapsed: 1.43609690666

Categories