Interview - Oracle - algorithm

In a game the only scores which can be made are 2,3,4,5,6,7,8 and they can be made any number of times
What are the total number of combinations in which the team can play and the score of 50 can be achieved by the team.
example 8,8,8,8,8,8,2 is valid 8,8,8,8,8,4,4,2 is also valid. etc...

The problem can be solved with dynamic programming, with 2 parameters:
i - the index up to which we have considered
s - the total score.
f(i, s) will contain the total number of ways to achieve score s.
Let score[] be the list of unique positive scores that can be made.
The formulation for the DP solution:
f(0, s) = 1, for all s divisible to score[0]
f(0, s) = 0, otherwise
f(i + 1, s) = Sum [for k = 0 .. floor(s/score[i + 1])] f(i, s - score[i + 1] * k)

This looks like a coin change problem. I wrote some Python code for it a while back.
Edited Solution:
from collections import defaultdict
my_dicto = defaultdict(dict)
def row_analysis(v, my_dicto, coins):
temp = 0
for coin in coins:
if v >= coin:
if v - coin == 0: # changed from if v - coin in (0, 1):
temp += 1
my_dicto[coin][v] = temp
else:
temp += my_dicto[coin][v - coin]
my_dicto[coin][v] = temp
else:
my_dicto[coin][v] = temp
return my_dicto
def get_combs(coins, value):
'''
Returns answer for coin change type problems.
Coins are assumed to be sorted.
Example:
>>> get_combs([1,2,3,5,10,15,20], 50)
2955
'''
dicto = defaultdict(dict)
for v in xrange(value + 1):
dicto = row_analysis(v, dicto, coins)
return dicto[coins[-1]][value]
In your case:
>>> get_combs([2,3,4,5,6,7,8], 50)
3095

It is like visit a 7-branches decision tree.
The code is:
class WinScore{
static final int totalScore=50;
static final int[] list={2,3,4,5,6,7,8};
public static int methodNum=0;
static void visitTree( int achieved , int index){
if (achieved >= totalScore ){
return;
}
for ( int i=index; i< list.length; i++ ){
if ( achieved + list[i] == totalScore ) {
methodNum++;
}else if ( achieved + list[i] < totalScore ){
visitTree( achieved + list[i], i );
}
}
}
public static void main( String[] args ){
visitTree(0, 0);
System.out.println("number of methods are:" + methodNum );
}
}
output:
number of methods are:3095

Just stumbled on this question - here's a c# variation which allows you to explore the different combinations:
static class SlotIterator
{
public static IEnumerable<string> Discover(this int[] set, int maxScore)
{
var st = new Stack<Slot>();
var combinations = 0;
set = set.OrderBy(c => c).ToArray();
st.Push(new Slot(0, 0, set.Length));
while (st.Count > 0)
{
var m = st.Pop();
for (var i = m.Index; i < set.Length; i++)
{
if (m.Counter + set[i] < maxScore)
{
st.Push(m.Clone(m.Counter + set[i], i));
}
else if (m.Counter + set[i] == maxScore)
{
m.SetSlot(i);
yield return m.Slots.PrintSlots(set, ++combinations, maxScore);
}
}
}
}
public static string PrintSlots(this int[] slots, int[] set, int numVariation, int maxScore)
{
var sb = new StringBuilder();
var accumulate = 0;
for (var j = 0; j < slots.Length; j++)
{
if (slots[j] <= 0)
{
continue;
}
var plus = "+";
for (var k = 0; k < slots[j]; k++)
{
accumulate += set[j];
if (accumulate == maxScore) plus = "";
sb.AppendFormat("{0}{1}", set[j], plus);
}
}
sb.AppendFormat("={0} - Variation nr. {1}", accumulate, numVariation);
return sb.ToString();
}
}
public class Slot
{
public Slot(int counter, int index, int countSlots)
{
this.Slots = new int[countSlots];
this.Counter = counter;
this.Index = index;
}
public void SetSlot(int index)
{
this.Slots[index]++;
}
public Slot Clone(int newval, int index)
{
var s = new Slot(newval, index, this.Slots.Length);
this.Slots.CopyTo(s.Slots, 0);
s.SetSlot(index);
return s;
}
public int[] Slots { get; private set; }
public int Counter { get; set; }
public int Index { get; set; }
}
Example:
static void Main(string[] args)
{
using (var sw = new StreamWriter(#"c:\test\comb50.txt"))
{
foreach (var s in new[] { 2, 3, 4, 5, 6, 7, 8 }.Discover(50))
{
sw.WriteLine(s);
}
}
}
Yields 3095 combinations.

Related

Suggestion to write a number as a sum of three numbers where each number is at least two

I'm trying to find all possible combinations to write a number as a sum of three numbers, where each number is at least 2.
So basically for 6 it would be like:
2 2 2
For 7 it would be:
2 2 3 or 2 3 2 or 3 2 2
I was wondering if there's a better approach to this than running 3 loops.
EDIT:
public class Problem {
public static void main(String args[]) {
int N = 7, n1, n2, n3;
for (n1 = 2; n1 <= N; n1++) {
for (n2 = 2; n2 <= N; n2++) {
for (n3 = 2; n3 <= N; n3++) {
if ( (n1+n2+n3)==N ) {
System.out.println(n1 + " " + n2 + " " + n3);
}
}
}
}
}
}
This solution works, but I was thinking if there's another approach to this.
You're looking to partition an integer n into k parts, where each part has a minimum value min. The classic approach here would be to use recursion.
Initially we create an array to hold the parts, initialize them to min and remove k*min from n to get the remainder.
static List<int[]> partitions(int n, int k, int min)
{
int[] parts = new int [k];
Arrays.fill(parts, min);
int rem = n - k*min;
List<int[]> results = new ArrayList<>();
partition(results, parts, rem);
return results;
}
We now use a recursive method to explore adding 1 to each of the parts in turn. If the remainder reaches 0 we have a solution and add the current solution to the results.
static void partition(List<int[]> results, int[] parts, int rem)
{
if(rem <= 0)
{
if(rem == 0) results.add(parts.clone());
return;
}
for(int i=0; i<parts.length; i++)
{
parts[i] += 1;
partition(results, parts, rem-1);
parts[i] -= 1;
}
}
Test:
public static void main(String[] args)
{
for(int[] res : partitions(7, 3, 2))
System.out.println(Arrays.toString(res));
}
Output:
[3, 2, 2]
[2, 3, 2]
[2, 2, 3]
it is my suggestion
create a class to model each answer of equation here we create a static inner class to model each answer:
public static class Answer{
private int a;
private int b;
private int c;
public Answer(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
public int getC() {
return c;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Answer answer = (Answer) o;
return a == answer.a &&
b == answer.b &&
c == answer.c;
}
#Override
public int hashCode() {
return Objects.hash(a, b, c);
}
#Override
public String toString() {
return "Answer{" +
"a=" + a +
", b=" + b +
", c=" + c +
'}';
}
}
then a recursive algorithm to produce results:
// don't call this method directly only combination method must call it
private Set<Answer> combinationHelper(int n){
Set<Answer> results = new HashSet<>();
if(n < 0)
return results;
if(n == 0) {
results.add(new Answer(0, 0, 0));
return results;
}
Set<Answer> remain = combinationHelper(n - 1);
remain.forEach(element -> {
int a = element.getA();
int b = element.getB();
int c = element.getC();
results.add(new Answer(a + 1, b, c));
results.add(new Answer(a, b + 1, c));
results.add(new Answer(a, b, c + 1));
});
return results;
}
public Set<Answer> combination(int n){
Set<Answer> results = combinationHelper(n - 6);
return results.stream()
.map(a -> new Answer(a.getA() + 2, a.getB() + 2, a.getC() + 2))
.collect(Collectors.toSet());
}
then call it like this:
System.out.println(new Comb().combination(6));
System.out.println(new Comb().combination(7));
which produces results:
[Answer{a=2, b=2, c=2}]
[Answer{a=2, b=3, c=2}, Answer{a=3, b=2, c=2}, Answer{a=2, b=2, c=3}]
if you don't like the idea of adding another model class to your application you can easily use arrays instead
non-recursive approach:
public List<String> combination(int n){
List<String> result = new ArrayList<>();
if(n < 6)
return result;
BitSet bits = new BitSet(n - 6);
bits.set(0, n - 6);
IntStream
.range(0, n - 5)
.forEach(i ->
IntStream.range(i, n - 5).forEach(j ->
result.add(String.format("%s %d %d %d %s", "{", bits.get(0, i).length() + 2, bits.get(i, j).length() + 2, bits.get(j, n).length() + 2, "}"))));
return result;
}
results for non-recursive approach:
for input 7:
[{ 2 2 3 }, { 2 3 2 }, { 3 2 2 }]
for input 6:
[{ 2 2 2 }]

Odd sum in array

You are given an array A containing N integers. You have to answer Q queries.
Queries are of form:
L R
Here you have to fInd sum of all numbers
, for those which has odd frequency in subarray L to R
First line of input contains a single integer N, next line contains N space separated integers, elements of array A. Next line of input contains a single integer Q. Q lines follow each containing two space separated integer L and R.
Sample Input
5
1 2 2 2 1
3
1 3
1 4
1 5
Sample Output
1
7
6
Explanation
For query 1: 1 has frequency 1 and 2 has frequency 2 so, answer is 1
For query 2: 1 has frequency 2 and 2 has frequency 3 So, answer is 7
-----------------------------Solution---------------------------------------
{{
public class samsungTest
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
int q = sc.nextInt();
for (int i = 0; i < q; i++) {
int l = sc.nextInt();
int r = sc.nextInt();
findOddSum(arr, l, r);
}
}
private static long findOddSum(int[] arr, int l, int r)
{
long sum = 0;
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = l-1; i < r; i++ ) {
if( map.get(arr[i]) == null ) {
map.put(arr[i], 1);
} else {
int freq = map.get(arr[i]);
map.put(arr[i], freq+1);
}
}
Set<Integer> freqset = map.keySet();
for (int val:freqset)
{
int freq = map.get(val);
if (freq%2 != 0) {
sum = sum+freq*val;
}
}
System.out.println(sum);
return sum;
}
}
}}
above solution is not right(as say HackerEarth). Please suggest me an efficient solution for this.
You got any solution for that as my solution is too getting failed in the hidden test cases.
package RD;
import java.util.HashMap;
import java.util.Scanner;
public class OddSum {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++)
{
arr[i]=sc.nextInt();
}
int query=sc.nextInt();
while(query>0)
{
HashMap<Integer, Integer> hs=new HashMap<>();
int left=sc.nextInt();
int right=sc.nextInt();
for(int i=left-1;i<right;i++)
{
if(!hs.containsKey(arr[i]))
{
hs.put(arr[i],1);
}
else
{
hs.put(arr[i],hs.get(arr[i])+1);
}
}
int sum=0;
for(Integer value:hs.keySet())
{
if(hs.get(value)%2!=0)
{
sum=sum+hs.get(value)*value;
}
}
System.out.println(sum);
query--;
}
}
}

To check an input string is present in custom table of strings

Assume length of element in table is 1 or 2.
Table: { h, fe, na, o}
input string: nafeo
Output: true
Table: {ab,bc}
input string: abc
Output: false
Please advise my below code will cover all the cases and is this the best solution? Or am I missing anything, any alternate solutions?
import java.util.*;
public class CustomTable {
Set<String> table = new HashSet<String>();
public CustomTable(){
// add your elements here for more test cases
table.add("oh");
table.add("he");
}
public int checkTable( String prev, String curr, String next) {
System.out.print(prev+":"+curr+":"+next);
System.out.println();
if (prev!=null) if (table.contains(prev)) return -1;
if (table.contains(curr)) return 0;
if (table.contains(next)) return 1;
return 2;
}
// ohhe.
public static void main(String args[]) {
CustomTable obj = new CustomTable();
String inputStr = "ohheo"; //Tested ohe,ohhe,ohohe
int result = 0;
String curr, prev, next;
for (int i = 0; i < inputStr.length(); i++) {
// if prev element is found
if (result==-1){
prev = null;
}
else {
if (i > 0) {
prev = inputStr.substring(i - 1, i + 1);
} else {
prev = inputStr.substring(i, i + 1);
}
}
curr = inputStr.substring(i,i+1);
if (i < inputStr.length()-1) {
next = inputStr.substring(i, i+2);
} else {
next = inputStr.substring(i, i+1);
}
result = obj.checkTable(prev, curr, next);
if (result==2) {
System.out.print("false");
return;
}
}
System.out.print("true");
}
}
I think the problem have similarities to well known subset sum problem and you can use its solutions by some customization.

Finding the index of the first word starting with a given alphabet form a alphabetically sorted list

Based on the current implementation, I will get an arraylist which contains some 1000 unique names in the alphabetically sorted order(A-Z or Z-A) from some source.
I need to find the index of the first word starting with a given alphabet.
So to be more precise, when I select an alphabet, for eg. "M", it should give me the index of the first occurrence of the word starting in "M" form the sorted list.
And that way I should be able to find the index of all the first words starting in each of the 26 alphabets.
Please help me find a solution which doesn't compromise on the speed.
UPDATE:
Actually after getting the 1000 unique names, the sorting is also done by one of my logics.
If this can be done while doing the sorting itself, I can avoid the reiteration on the list after sorting to find the indices for the alphabets.
Is that possible?
Thanks,
Sen
I hope this little piece of code will help you. I guessed the question is related to Java, because you mentioned ArrayList.
String[] unsorted = {"eve", "bob", "adam", "mike", "monica", "Mia", "marta", "pete", "Sandra"};
ArrayList<String> names = new ArrayList<String>(Arrays.asList(unsorted));
String letter = "M"; // find index of this
class MyComp implements Comparator<String>{
String first = "";
String letter;
MyComp(String letter){
this.letter = letter.toUpperCase();
}
public String getFirst(){
return first;
}
#Override
public int compare(String s0, String s1) {
if(s0.toUpperCase().startsWith(letter)){
if(s0.compareTo(first) == -1 || first.equals("")){
first = s0;
}
}
return s0.toUpperCase().compareTo(s1.toUpperCase());
}
};
MyComp mc = new MyComp(letter);
Collections.sort(names, mc);
int index = names.indexOf(mc.getFirst()); // the index of first name starting with letter
I'm not sure if it's possible to also store the index of the first name in the comparator without much overhead. Anyway, if you implement your own version of sorting algorithm e.g. quicksort, you should know about the index of the elements and could calculate the index while sorting. This depends on your chosen sorting algorithm and implementation. In fact if I know how your sorting is implemented, we could insert the index calculation.
So I came up with my own solution for this.
package test.binarySearch;
import java.util.Random;
/**
*
* Binary search to find the index of the first starting in an alphabet
*
* #author Navaneeth Sen <navaneeth.sen#multichoice.co.za>
*/
class SortedWordArray
{
private final String[] a; // ref to array a
private int nElems; // number of data items
public SortedWordArray(int max) // constructor
{
a = new String[max]; // create array
nElems = 0;
}
public int size()
{
return nElems;
}
public int find(String searchKey)
{
return recFind(searchKey, 0, nElems - 1);
}
String array = null;
int arrayIndex = 0;
private int recFind(String searchKey, int lowerBound,
int upperBound)
{
int curIn;
curIn = (lowerBound + upperBound) / 2;
if (a[curIn].startsWith(searchKey))
{
array = a[curIn];
if ((curIn == 0) || !a[curIn - 1].startsWith(searchKey))
{
return curIn; // found it
}
else
{
return recFind(searchKey, lowerBound, curIn - 1);
}
}
else if (lowerBound > upperBound)
{
return -1; // can't find it
}
else // divide range
{
if (a[curIn].compareTo(searchKey) < 0)
{
return recFind(searchKey, curIn + 1, upperBound);
}
else // it's in lower half
{
return recFind(searchKey, lowerBound, curIn - 1);
}
} // end else divide range
} // end recFind()
public void insert(String value) // put element into array
{
int j;
for (j = 0; j < nElems; j++) // find where it goes
{
if (a[j].compareTo(value) > 0) // (linear search)
{
break;
}
}
for (int k = nElems; k > j; k--) // move bigger ones up
{
a[k] = a[k - 1];
}
a[j] = value; // insert it
nElems++; // increment size
} // end insert()
public void display() // displays array contents
{
for (int j = 0; j < nElems; j++) // for each element,
{
System.out.print(a[j] + " "); // display it
}
System.out.println("");
}
} // end class OrdArray
class BinarySearchWordApp
{
static final String AB = "12345aqwertyjklzxcvbnm";
static Random rnd = new Random();
public static String randomString(int len)
{
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++)
{
sb.append(AB.charAt(rnd.nextInt(AB.length())));
}
return sb.toString();
}
public static void main(String[] args)
{
int maxSize = 100000; // array size
SortedWordArray arr; // reference to array
int[] indices = new int[27];
arr = new SortedWordArray(maxSize); // create the array
for (int i = 0; i < 100000; i++)
{
arr.insert(randomString(10)); //insert it into the array
}
arr.display(); // display array
String searchKey;
for (int i = 97; i < 124; i++)
{
searchKey = (i == 123)?"1":Character.toString((char) i);
long time_1 = System.currentTimeMillis();
int result = arr.find(searchKey);
long time_2 = System.currentTimeMillis() - time_1;
if (result != -1)
{
indices[i - 97] = result;
System.out.println("Found " + result + "in "+ time_2 +" ms");
}
else
{
if (!(i == 97))
{
indices[i - 97] = indices[i - 97 - 1];
}
System.out.println("Can't find " + searchKey);
}
}
for (int i = 0; i < indices.length; i++)
{
System.out.println("Index [" + i + "][" + (char)(i+97)+"] = " + indices[i]);
}
} // end main()
}
All comments welcome.

Finding the longest repeated substring

What would be the best approach (performance-wise) in solving this problem?
I was recommended to use suffix trees. Is this the best approach?
Check out this link: http://introcs.cs.princeton.edu/java/42sort/LRS.java.html
/*************************************************************************
* Compilation: javac LRS.java
* Execution: java LRS < file.txt
* Dependencies: StdIn.java
*
* Reads a text corpus from stdin, replaces all consecutive blocks of
* whitespace with a single space, and then computes the longest
* repeated substring in that corpus. Suffix sorts the corpus using
* the system sort, then finds the longest repeated substring among
* consecutive suffixes in the sorted order.
*
* % java LRS < mobydick.txt
* ',- Such a funny, sporty, gamy, jesty, joky, hoky-poky lad, is the Ocean, oh! Th'
*
* % java LRS
* aaaaaaaaa
* 'aaaaaaaa'
*
* % java LRS
* abcdefg
* ''
*
*************************************************************************/
import java.util.Arrays;
public class LRS {
// return the longest common prefix of s and t
public static String lcp(String s, String t) {
int n = Math.min(s.length(), t.length());
for (int i = 0; i < n; i++) {
if (s.charAt(i) != t.charAt(i))
return s.substring(0, i);
}
return s.substring(0, n);
}
// return the longest repeated string in s
public static String lrs(String s) {
// form the N suffixes
int N = s.length();
String[] suffixes = new String[N];
for (int i = 0; i < N; i++) {
suffixes[i] = s.substring(i, N);
}
// sort them
Arrays.sort(suffixes);
// find longest repeated substring by comparing adjacent sorted suffixes
String lrs = "";
for (int i = 0; i < N - 1; i++) {
String x = lcp(suffixes[i], suffixes[i+1]);
if (x.length() > lrs.length())
lrs = x;
}
return lrs;
}
// read in text, replacing all consecutive whitespace with a single space
// then compute longest repeated substring
public static void main(String[] args) {
String s = StdIn.readAll();
s = s.replaceAll("\\s+", " ");
StdOut.println("'" + lrs(s) + "'");
}
}
Have a look at http://en.wikipedia.org/wiki/Suffix_array as well - they are quite space-efficient and have some reasonably programmable algorithms to produce them, such as "Simple Linear Work Suffix Array Construction" by Karkkainen and Sanders
Here is a simple implementation of longest repeated substring using simplest suffix tree. Suffix tree is very easy to implement in this way.
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
using namespace std;
class Node
{
public:
char ch;
unordered_map<char, Node*> children;
vector<int> indexes; //store the indexes of the substring from where it starts
Node(char c):ch(c){}
};
int maxLen = 0;
string maxStr = "";
void insertInSuffixTree(Node* root, string str, int index, string originalSuffix, int level=0)
{
root->indexes.push_back(index);
// it is repeated and length is greater than maxLen
// then store the substring
if(root->indexes.size() > 1 && maxLen < level)
{
maxLen = level;
maxStr = originalSuffix.substr(0, level);
}
if(str.empty()) return;
Node* child;
if(root->children.count(str[0]) == 0) {
child = new Node(str[0]);
root->children[str[0]] = child;
} else {
child = root->children[str[0]];
}
insertInSuffixTree(child, str.substr(1), index, originalSuffix, level+1);
}
int main()
{
string str = "banana"; //"abcabcaacb"; //"banana"; //"mississippi";
Node* root = new Node('#');
//insert all substring in suffix tree
for(int i=0; i<str.size(); i++){
string s = str.substr(i);
insertInSuffixTree(root, s, i, s);
}
cout << maxLen << "->" << maxStr << endl;
return 1;
}
/*
s = "mississippi", return "issi"
s = "banana", return "ana"
s = "abcabcaacb", return "abca"
s = "aababa", return "aba"
*/
the LRS problem is one that is best solved using either a suffix tree or a suffix array. Both approaches have a best time complexity of O(n).
Here is an O(nlog(n)) solution to the LRS problem using a suffix array. My solution can be improved to O(n) if you have a linear construction time algorithm for the suffix array (which is quite hard to implement). The code was taken from my library. If you want more information on how suffix arrays work make sure to check out my tutorials
/**
* Finds the longest repeated substring(s) of a string.
*
* Time complexity: O(nlogn), bounded by suffix array construction
*
* #author William Fiset, william.alexandre.fiset#gmail.com
**/
import java.util.*;
public class LongestRepeatedSubstring {
// Example usage
public static void main(String[] args) {
String str = "ABC$BCA$CAB";
SuffixArray sa = new SuffixArray(str);
System.out.printf("LRS(s) of %s is/are: %s\n", str, sa.lrs());
str = "aaaaa";
sa = new SuffixArray(str);
System.out.printf("LRS(s) of %s is/are: %s\n", str, sa.lrs());
str = "abcde";
sa = new SuffixArray(str);
System.out.printf("LRS(s) of %s is/are: %s\n", str, sa.lrs());
}
}
class SuffixArray {
// ALPHABET_SZ is the default alphabet size, this may need to be much larger
int ALPHABET_SZ = 256, N;
int[] T, lcp, sa, sa2, rank, tmp, c;
public SuffixArray(String str) {
this(toIntArray(str));
}
private static int[] toIntArray(String s) {
int[] text = new int[s.length()];
for(int i=0;i<s.length();i++)text[i] = s.charAt(i);
return text;
}
// Designated constructor
public SuffixArray(int[] text) {
T = text;
N = text.length;
sa = new int[N];
sa2 = new int[N];
rank = new int[N];
c = new int[Math.max(ALPHABET_SZ, N)];
construct();
kasai();
}
private void construct() {
int i, p, r;
for (i=0; i<N; ++i) c[rank[i] = T[i]]++;
for (i=1; i<ALPHABET_SZ; ++i) c[i] += c[i-1];
for (i=N-1; i>=0; --i) sa[--c[T[i]]] = i;
for (p=1; p<N; p <<= 1) {
for (r=0, i=N-p; i<N; ++i) sa2[r++] = i;
for (i=0; i<N; ++i) if (sa[i] >= p) sa2[r++] = sa[i] - p;
Arrays.fill(c, 0, ALPHABET_SZ, 0);
for (i=0; i<N; ++i) c[rank[i]]++;
for (i=1; i<ALPHABET_SZ; ++i) c[i] += c[i-1];
for (i=N-1; i>=0; --i) sa[--c[rank[sa2[i]]]] = sa2[i];
for (sa2[sa[0]] = r = 0, i=1; i<N; ++i) {
if (!(rank[sa[i-1]] == rank[sa[i]] &&
sa[i-1]+p < N && sa[i]+p < N &&
rank[sa[i-1]+p] == rank[sa[i]+p])) r++;
sa2[sa[i]] = r;
} tmp = rank; rank = sa2; sa2 = tmp;
if (r == N-1) break; ALPHABET_SZ = r + 1;
}
}
// Use Kasai algorithm to build LCP array
private void kasai() {
lcp = new int[N];
int [] inv = new int[N];
for (int i = 0; i < N; i++) inv[sa[i]] = i;
for (int i = 0, len = 0; i < N; i++) {
if (inv[i] > 0) {
int k = sa[inv[i]-1];
while( (i + len < N) && (k + len < N) && T[i+len] == T[k+len] ) len++;
lcp[inv[i]-1] = len;
if (len > 0) len--;
}
}
}
// Finds the LRS(s) (Longest Repeated Substring) that occurs in a string.
// Traditionally we are only interested in substrings that appear at
// least twice, so this method returns an empty set if this is not the case.
// #return an ordered set of longest repeated substrings
public TreeSet <String> lrs() {
int max_len = 0;
TreeSet <String> lrss = new TreeSet<>();
for (int i = 0; i < N; i++) {
if (lcp[i] > 0 && lcp[i] >= max_len) {
// We found a longer LRS
if ( lcp[i] > max_len )
lrss.clear();
// Append substring to the list and update max
max_len = lcp[i];
lrss.add( new String(T, sa[i], max_len) );
}
}
return lrss;
}
public void display() {
System.out.printf("-----i-----SA-----LCP---Suffix\n");
for(int i = 0; i < N; i++) {
int suffixLen = N - sa[i];
String suffix = new String(T, sa[i], suffixLen);
System.out.printf("% 7d % 7d % 7d %s\n", i, sa[i],lcp[i], suffix );
}
}
}
public class LongestSubString {
public static void main(String[] args) {
String s = findMaxRepeatedString("ssssssssssss this is a ddddddd word with iiiiiiiiiis and loads of these are ppppppppppppps");
System.out.println(s);
}
private static String findMaxRepeatedString(String s) {
Processor p = new Processor();
char[] c = s.toCharArray();
for (char ch : c) {
p.process(ch);
}
System.out.println(p.bigger());
return new String(new char[p.bigger().count]).replace('\0', p.bigger().letter);
}
static class CharSet {
int count;
Character letter;
boolean isLastPush;
boolean assign(char c) {
if (letter == null) {
count++;
letter = c;
isLastPush = true;
return true;
}
return false;
}
void reassign(char c) {
count = 1;
letter = c;
isLastPush = true;
}
boolean push(char c) {
if (isLastPush && letter == c) {
count++;
return true;
}
return false;
}
#Override
public String toString() {
return "CharSet [count=" + count + ", letter=" + letter + "]";
}
}
static class Processor {
Character previousLetter = null;
CharSet set1 = new CharSet();
CharSet set2 = new CharSet();
void process(char c) {
if ((set1.assign(c)) || set1.push(c)) {
set2.isLastPush = false;
} else if ((set2.assign(c)) || set2.push(c)) {
set1.isLastPush = false;
} else {
set1.isLastPush = set2.isLastPush = false;
smaller().reassign(c);
}
}
CharSet smaller() {
return set1.count < set2.count ? set1 : set2;
}
CharSet bigger() {
return set1.count < set2.count ? set2 : set1;
}
}
}
I had an interview and I needed to solve this problem. This is my solution:
public class FindLargestSubstring {
public static void main(String[] args) {
String test = "ATCGATCGA";
System.out.println(hasRepeatedSubString(test));
}
private static String hasRepeatedSubString(String string) {
Hashtable<String, Integer> hashtable = new Hashtable<>();
int length = string.length();
for (int subLength = length - 1; subLength > 1; subLength--) {
for (int i = 0; i <= length - subLength; i++) {
String sub = string.substring(i, subLength + i);
if (hashtable.containsKey(sub)) {
return sub;
} else {
hashtable.put(sub, subLength);
}
}
}
return "No repeated substring!";
}}
There are way too many things that affect performance for us to answer this question with only what you've given us. (Operating System, language, memory issues, the code itself)
If you're just looking for a mathematical analysis of the algorithm's efficiency, you probably want to change the question.
EDIT
When I mentioned "memory issues" and "the code" I didn't provide all the details. The length of the strings you will be analyzing are a BIG factor. Also, the code doesn't operate alone - it must sit inside a program to be useful. What are the characteristics of that program which impact this algorithm's use and performance?
Basically, you can't performance tune until you have a real situation to test. You can make very educated guesses about what is likely to perform best, but until you have real data and real code, you'll never be certain.

Resources