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
Related
The question is -
The arithmetic sequence, 1487, 4817, 8147, in which each of the terms
increases by 3330, is unusual in two ways: (i) each of the three terms
are prime, and, (ii) each of the 4-digit numbers are permutations of
one another.
There are no arithmetic sequences made up of three 1-, 2-, or 3-digit
primes, exhibiting this property, but there is one other 4-digit
increasing sequence.
What 12-digit number do you form by concatenating the three terms in
this sequence?
I've written this code -
package Problems;
import java.util.ArrayList;
import java.util.LinkedList;
public class Pro49 {
private static boolean isPrime(int n){
if(n%2 == 0) return false;
for(int i = 3; i<= Math.sqrt(n); i++){
if(n%i == 0) return false;
}
return true;
}
private static boolean isPerm(int m, int n){
ArrayList<Integer> mArr = new ArrayList<>();
ArrayList<Integer> nArr = new ArrayList<>();
for(int i = 0; i<4; i++){
mArr.add(m%10);
m /= 10;
}
for(int i = 0; i<4; i++){
nArr.add(n%10);
n /= 10;
}
return mArr.containsAll(nArr);
}
public static void main(String[] args) {
LinkedList<Integer> primes = new LinkedList<>();
for(int i = 1001; i<10000; i++){
if(isPrime(i)) primes.add(i);
}
int k = 0;
boolean breaker = false;
for(int i = 0; i<primes.size() - 2; i++){
for(int j = i + 1; j<primes.size() - 1; j++){
if(isPerm(primes.get(i), primes.get(j))) {
k = primes.get(j) + (primes.get(j) - primes.get(i));
if(k<10000 && primes.contains(k) && isPerm(primes.get(i), k)) {
System.out.println(primes.get(i) + "\n" + primes.get(j) + "\n" + k);
breaker = true;
break;
}
}
if(breaker) break;
}
if(breaker) break;
}
}
}
I added the print line System.out.println(primes.get(i) + "\n" + primes.get(j) + "\n" + k); to check the numbers. I got 1049, 1499, 1949 which are wrong. (At least 1049 is wrong I guess).
Can any one point out where my code/logic is wrong?
Any help is appreciated.
I think where your logic is going wrong is your isPerm method. You are using AbstractCollection#containsAll, which, AFAIK, only checks if the parameters are in the collection at least once.
i.e. it basically does
for(E e : collection)
if(!this.contains(e)) return false;
return true;
Therefore, for example, 4999 will be a permutation of 49 because 49 contains 4 and 9 (while it is clearly not based on your example).
The reason why your method seems to work for these values is that you are looping a fixed amount of time - that is, 4. For a number like 49 you will end up with {9, 4, 0, 0} instead of {9, 4}. Do something like this:
while(n != 0) {
nArr.add(n%10);
n /= 10;
}
and you will get the correct digit Lists (and see that containsAll won't work.)
Add the 4-digit restriction elsewhere (e.g. in your loop.)
Maybe you could check the occurrences per digit.
For example:
int[] occurrencesA = new int[10], occurrencesB = new int[10];
for(; m != 0; m /= 10)
occurrencesA[m % 10]++;
for(; n != 0; n /= 10)
occurrencesB[n % 10]++;
for(int i = 0; i < 10; i++)
if(occurrencesA[i] != occurrencesB[i]) return false;
return true;
I found a possible alternative for isPerm
private static boolean isPerm(int m, int n){
ArrayList<Integer> mArr = new ArrayList<>();
ArrayList<Integer> nArr = new ArrayList<>();
final String mS = Integer.toString(m);
final String nS = Integer.toString(n);
if(mS.length() != nS.length()) return false;
for(int i = 0; i<mS.length(); i++){
mArr.add(m%10);
m /= 10;
}
for(int i = 0; i<nS.length(); i++){
nArr.add(n%10);
n /= 10;
}
return (mArr.containsAll(nArr) && nArr.containsAll(mArr));
}
This is giving me the correct answer. Another alternative is posted by some other person below.
I am currently creating a program where the user inputs an array of integers. The program has to find the longest increasing sequence of consecutive elements. So if the user enters "3,2,1,2,4,6,7,8,1,2" the program will output "1,2,4,5,6,7,8". However, I keep running into 2 errors.
The first error is that when xs = 1000,97777,487,8274,972837. The program will output "1000,97777" instead of "487,8274,972837". Logically this is wrong since the first output is not the "LONGEST" increasing sequence of consecutive element.
The second error is that when xs = 2,7. It seems to output an empty array instead of "2,7". I'm assuming it's because there aren't enough elements perhaps?
static int[] increasing(int[] xs){
ArrayList<Integer> current_array = new ArrayList<Integer>();
ArrayList<Integer> list = new ArrayList<Integer>();
int c_counter = 0;
for (int i = 0; i<(xs.length); i++){
if (i==0){
if (xs[i+1] > xs[i]){
current_array.add(xs[i]);
c_counter++; //keeps track of how many elements have been added
}
}
else if ((xs[i] > xs[i-1])){
if (c_counter==0){
current_array.add(xs[i-1]); //makes sure the smaller number gets added too
current_array.add(xs[i]);
c_counter = c_counter + 2;
} else{
current_array.add(xs[i]);
c_counter++;
}
} else {
if (current_array.size()>list.size()){ //compares sizes to find the longest sequence
list.clear();
for (int k=0; k<(current_array.size()); k++){
if (current_array.get(k) != 0){ //removes any null values
list.add(current_array.get(k));
}
}
current_array.clear(); //clears it to restart and find any longer sequences
c_counter = 0;
}
}
}
int[] out_array = list.stream().mapToInt(i->i).toArray(); //converts from arraylist to int[] as that's the format it must output
out_array = list.stream().filter(i->i != null).mapToInt(i->i).toArray();
return out_array;
}
public static int[] increasing(int[] xs){
int start = 0;
int end = 0;
int temp = 0;
for (int i = 0; i < xs.length; i++) {
if(i==0 || xs[i]<xs[i-1]){
temp = i;
}
else if(i-temp > end-start){
start = temp;
end = i;
}
}
return Arrays.copyOfRange(xs, start, end+1);
}
It takes too long time to understand your code. Try this out:
List<Integer> test = new ArrayList<Integer>();
test.add(3);
test.add(2);
test.add(1);
test.add(2);
// test.add(4);
// test.add(6);
// test.add(7);
// test.add(8);
test.add(1);
test.add(2);
test.add(1000);
test.add(97777);
test.add(487);
test.add(8274);
test.add(972837);
List<Integer> output = new ArrayList<Integer>();
List<Integer> temp = new ArrayList<Integer>();
for(int i = 0; i < test.size(); i++) {
int current = test.get(i);
int next = Integer.MIN_VALUE;
if(i + 1 < test.size()) next = test.get(i + 1);
if(current > next) {
if(output.size() <= temp.size()) {
temp.add(current);
output = new ArrayList<Integer>(temp);
}
temp.clear();
} else {
temp.add(current);
}
}
output.forEach(i -> System.out.print(i + ", "));
If you want the longest decreasing output, change int next = Integer.MAX_VALUE; and if(current > next) to if(current < next)
I have an input list of words. You check the suffix of the first word to the prefix of the next word.
Eg.
serene
next
tango
extra
{serene,next}= 2common letters {serene,tango}=0 {serene,extra}= 1
{next,serene}= 0 {next,tango}= 1 {next,extra}= 3
{tango,serene}=0 {tango,next}= 0 {tango,extra}= 0
{extra,serene}=0 {extra,next}=0 {extra,tango}=0
You can also switch the order of the words i.e.(next, serene) if overlap letter score is better this way
so you check the overlap scores with each word and finally return the list of words with maximal score
Going by the input list the score is 1
serene,next,tango,extra = 1
Maximal Score is = 5 and the output list returned would be the following:
serine,next,extra,tango
serene,next= 2common letters serene,tango=0 serene,extra= 1
next,serene= 0 next,tango= 1 next,extra= 3
tango,serene=0 tango,next= 0 tango,extra= 0
extra,serene=0 extra,next=0 extra,tango=0
What is the best way to calculate overlap score and return maximal score list in terms of complexity?
I am only able to calculate the overlap score for consecutive words, but that doesn't give maximal score.
You can add all the letters in a list and then do retainAll like:
String one="next", two="extra";
List<Character> oneList=new ArrayList<Character>();
for(Character c : one.toCharArray()) {
oneList.add(c);
}
List<Character> twoList=new ArrayList<Character>();
for(Character c : two.toCharArray()) {
twoList.add(c);
}
List<Character> finalList = new ArrayList<Character>(oneList);
finalList.retainAll(twoList);
System.out.print("There are "+finalList.size()+ " letters in common and they are : ");
for(Character c: finalList){
System.out.print(c+" ");
}
Unfortunately I don't know a better way to convert primitive data type into list other that using Google Guava library or other 3 party API's. If you want to optimize the code then looking them.
I am not sure it is the most efficient approach, but I would compute the matrix of scores for any two consecutive words, and then simply use backtrack to find the longest possible chain.
Backtracking has a poor efficiency reputation, but in current use case, I think it can be used, because we can stop analyze as soon as 2 words have a score of 0. So I can find the correct maximal score 5 and the best sequence in 11 operations.
Code :
public class Overlap {
int[][] matrix;
int total;
int [] bestSeq;
String[] strings;
/**
* #param args the command line arguments
*/
public static void main(String[] strings) {
// TODO code application logic here
Overlap overlap = new Overlap(strings);
int score = overlap.compute();
System.out.println("Best score : " + score);
for (int i : overlap.bestSeq) {
System.out.print(" " + strings[i]);
}
System.out.println(" in " + overlap.total + " operations");
}
public Overlap(String[] strings) {
this.strings = strings;
matrix = matrix(strings);
bestSeq = new int[strings.length];
}
int compute() {
total = 0;
int[] sequence = new int[strings.length];
for (int i=0; i < strings.length; i++) {
sequence[i] = i;
}
return this.bestSequence(-1, sequence, bestSeq);
}
static int findOverlap(String a, String b) {
int la = a.length();
int l = Math.min(la, b.length());
while (l > 0) {
if (a.substring(la - l).equals(b.substring(0, l))) {
return l;
}
l--;
}
return 0;
}
static int[][] matrix(String[] strings) {
int l = strings.length;
int[][] mx = new int[l][l];
for (int i = 0; i < l - 1; i++) {
for (int j = i + 1; j < l; j++) {
mx[i][j] = findOverlap(strings[i], strings[j]);
}
}
return mx;
}
int bestSequence(int initial, int[] sequence, int[] best) {
total += 1;
int max = 0;
if (best.length != sequence.length) {
throw new java.lang.IllegalArgumentException();
}
int l = sequence.length;
int[] newseq = new int[l - 1];
int[] newbest = new int[l - 1];
for (int i : sequence) {
int val = (initial == -1) ? 0 : matrix[initial][i];
if ((val > 0) || (initial == -1)) {
int k = 0;
for (int j : sequence) {
if (j != i) {
newseq[k++] = j;
}
}
val += bestSequence(i, newseq, newbest);
if (val > max) {
max = val;
best[0] = i;
System.arraycopy(newbest, 0, best, 1, l - 1);
}
}
}
if (max == 0) {
System.arraycopy(sequence, 0, best, 0, l);
}
return max;
}
}
With the arguments serene next tango extra, it prints :
Best score : 5
serene next extra tango
in 11 operations
I'm pretty rusty on my Java skills but I was trying to write a program that prompts the user to enter a string and displays a maximum length increasing ordered subsequence of characters. For example, if the user entered Welcome the program would output Welo. If the user entered WWWWelllcommmeee, the program would still output Welo. I've gotten this much done but it's not doing what it should be and I'm honestly at a loss as to why.
import java.util.ArrayList;
import java.util.Scanner;
public class Stuff {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Please enter a string. ");
String userString = input.next();
ArrayList charList = new ArrayList();
ArrayList finalList = new ArrayList();
int currentLength = 0;
int max = 0;
for(int i = 0; i < userString.length(); i++){
charList.add(userString.charAt(i));
for(int j = i; j < userString.length(); j++){
int k=j+1;
if(k < userString.length() && userString.charAt(k) > userString.charAt(j)){
charList.add(userString.charAt(j));
currentLength++;
}
}
}
if(max < currentLength){
max = currentLength;
finalList.addAll(charList);
}
for (int i = 0; i < finalList.size(); i++){
char item = (char) finalList.get(i);
System.out.print(item);
}
int size1 = charList.size();
int size2 = finalList.size();
System.out.println("");
System.out.println("Size 1 is: " + size1 + " Size 2 is : " + size2);
}
}
My code, if I input Welcome, outputs WWeceeclcccome.
Does anyone have some tips on what I'm doing wrong?
In these cases it tends to help to step away from the keyboard and think about the algorithm you're trying to implement. Try to explain it first in words.
You are constructing a list of individual characters by appending each of the characters in the input string followed by characters to its right that are in correct alphabetical with their successor. For the input "Welcome" this means the accumulated output will be, showing the outer loop in vertical and inner loop in horizontal:
W W e c
e e c
l c
c c
o
m
e
In total: WWeceeclccome
I can't see the logic of this implementation. Here is a faster solution which runs in O(nlogn) time.
import java.util.Scanner;
public class Stuff
{
//return the index of the first element that's not less than the target element
public static int bsearch(char[] arr, int size, int key)
{
int left = 0;
int right = size - 1;
int mid;
while (left <= right)
{
mid = (left + right) / 2;
if(arr[mid] < key)
left = mid + 1;
else
right = mid - 1;
}
return left;
}
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
System.out.println("Please enter a string: ");
String userString = input.next();
char[] maxArr = new char[userString.length()];
char[] precedent = new char[userString.length()];
maxArr[0] = userString.charAt(0);
precedent[0] = userString.charAt(0);
int len = 1;
for(int i = 1; i < userString.length(); i++)
{
if(userString.charAt(i) > maxArr[len - 1])
{
maxArr[len] = userString.charAt(i);
precedent[len] = userString.charAt(i);
len++;
}
else
maxArr[bsearch(maxArr, len, userString.charAt(i))] = userString.charAt(i);
}
//System.out.println(len);
for(int i = 0; i < len; i++)
System.out.print(precedent[i]);
}
}
Using Dynamic Programming O(N^2) in lexicography order mean if i/p is abcbcbcd then o/p can be abcccd, abbbcd, abbccd but as per lexicography order o/p will be abbbcd.
public static String longestIncreasingSubsequence(String input1) {
int dp[] = new int[input1.length()];
int i,j,max = 0;
StringBuilder str = new StringBuilder();
/* Initialize LIS values for all indexes */
for ( i = 0; i < input1.length(); i++ )
dp[i] = 1;
/* Compute optimized LIS values in bottom up manner */
for ( i = 1; i < input1.length(); i++ )
for ( j = 0; j < i; j++ )
if (input1.charAt(i) >= input1.charAt(j) && dp[i] < dp[j]+1)
dp[i] = dp[j] + 1;
/* Pick maximum of all LIS values */
for ( i = 0; i < input1.length(); i++ ) {
if ( max < dp[i] ) {
max = dp[i];
if (i + 1 < input1.length() && max == dp[i+1] && input1.charAt(i+1) < input1.charAt(i)) {
str.append(input1.charAt(i+1));
i++;
} else {
str.append(input1.charAt(i));
}
}
}
return str.toString();
}
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;
}