How to modify my code to remove (not generate) duplicated permutations - java

I cannot figure out how to recognize that I generate repeated permutation in a recursive call. Let's say we 2 repeated letters in a string of length n. Then I need to create n!/2! sequences, instead of n! sequences.
How to modify my code to achieve this?
public class GeneralPermutationGenerator{
public static void main(String[] args) {
String s = "AABC";
perm(s);
}
public static void perm(String s){
char cs[] = s.toCharArray();
char result[] = new char[cs.length];
rperm(cs, result, 0);
}
static int j = 1;
private static void rperm(char[] cs, char[] result, int level){
if(level == result.length){
System.out.println(j++ + " " + new String(result));
return;
}
for(int i = 0; i < cs.length; i++){
if(cs[i] != 0){
result[level] = cs[i];
char temp = cs[i];
cs[i] = 0;
rperm(cs, result, ++level);
cs[i] = temp;
level--;
}
}
}
}

The uniqueness can be enforced by always taking a letter that appears multiple times from the first position available.
That is, at each level, when choosing a letter, you can look backward and see if it already occurred in the cs array. If it did occur before (which means it was not selected yet, because that position in cs is not zero), then it should not be allowed to select it from this position.
Implementation
One possible implementation involves changing the rperm code as follows (looping through the previous characters, to see if the current char was already encountered):
private static void rperm(char[] cs, char[] result, int level) {
if (level == result.length) {
System.out.println(j++ + " " + new String(result));
return;
}
for (int i = 0; i < cs.length; i++) {
if (cs[i] != 0) {
// first, determine if the current char was already
// encountered among the available options
boolean encountered = false;
for (int j = 0; j < i; j++) {
if (cs[j] == cs[i]) {
encountered = true;
break;
}
}
if (!encountered) {
result[level] = cs[i];
char temp = cs[i];
cs[i] = 0;
rperm(cs, result, ++level);
cs[i] = temp;
level--;
}
}
}
}
Explanation
To see how this works, consider again the example AABC.
To differentiate the two As in this discussion, let us denote them as A1 and A2.
For level = 0, we should choose a character to be put into result[0]:
we can choose A1;
we can NOT choose A2, because there was already an A encountered before in the list of available chars for this level;
we can choose B;
we can choose C.
First, the algorithm will choose A1, and proceed with recursion at next level.
At level = 1.
Now, the position associated to A1 has been marked with a 0 in the ch array.
Thus we have the following alternatives for the character to be put in result[1]:
choose A2 (because now there is not an A available before, as the first one was already taken at the previous recursion level, and marked with 0)
choose B;
choose C.
It will first select A2, and the partial permutation so far will be A1 A2, with two more levels to go in the recursion. However, the key for not having duplicates is that for a same character, its indices will always be in increasing order. The algorithm will not be able to also generate a permutation starting with A2 A1, simply because A2 is not allowed to be chosen if A1 is still available.

There is a simple non-recursive algorithm for finding the lexicographically next permutation of a sequence:
Scan backwards from the end of the sequence until you find an element which is (strictly) less than the following one. If there isn't one, the sequence is the lexicographically greatest possible permutation.
Reverse the subsequence of elements following the one which you found.
Exchange the element you found in step 1 with the first following element which is (strictly) greater than it.
I'm not really a Java programmer, so here's an implementation in simplified C++, using fewer standard library functions than I would usually use in the hopes that it is easier to understand:
template<typename V>
bool nextPerm(V& v) {
for (auto i = v.size(); i > 1; --i)
if (v[i-2] < v[i-1]) {
std::reverse(v.begin() + i - 1, v.end());
for (auto j = i - 1; j < v.size(); ++j)
if (v[i-2] < v[j]) { std::swap(v[i-2], v[j]); break; }
return true;
}
return false;
}

Related

How do I manually find a substring in a string? (Java)

public int lookFor(String s) {
final int EXIST = 1;
final int NOT_EXIST = -1;
int thisIndex = 0;
int otherIndex = 0;
char thisNext;
char otherNext;
if (s == null || s.length() == 0)
return NOT_EXIST;
for(; thisIndex < this.mainString.length() ; ) {
thisNext = this.mainString.charAt(thisIndex);
otherNext = s.charAt(otherIndex);
if (thisNext == otherNext) {
thisIndex++;
otherIndex++;
}
else if (thisNext != otherNext)
thisIndex++;
if (otherIndex == s.length()-1)
return EXIST;
}
return NOT_EXIST;
}
This is my attempt so far.
mainString = the main string I want to find the substring in.
s = the substring.
So my idea was to get the first chars of both strings, see if they equal. if they don't, i'll get the second char of mainString, see if they equal (mainString second char to s first char). If they're not equal, i'll get the third char of mainString and so forth. Once they're equal, i'll get the next char of both strings and see if they both equal.
Basically the loops knows that mainString contains s when index of s equals to s length minus one (that means the loop looped all the way to the last char inc, of s, so s index == s length -1).
Is the logic I'm trying to work with incorrect? or I just executed it not good? i'll happy to get answers!
Here's my naïve approach:
private final int EXIST = 1;
private final int NOT_EXIST = -1;
private int lookFor(String a, String b, int index) {
for (int i = 0; i < b.length(); i++) {
if ((index + i) >= a.length()) return NOT_EXIST;
if (a.charAt(index + i) != b.charAt(i)) return NOT_EXIST;
}
return EXIST;
}
public int lookFor(String a, String b) {
char start = b.charAt(0);
for (int i=0; i < a.length(); i++) {
if (a.charAt(i) == start) {
if (lookFor(a, b, i) == EXIST) return EXIST;
}
}
return NOT_EXIST;
}
Though, I'm not sure why you would do this when you could just do:
int ret = a.contains(b) ? EXIST : NOT_EXIST
However I wanted to actually answer your question.
Here's a slightly improved version that satisfies your "all in one method" requirement.
public static int lookFor(String a, String b) {
// Fancy way of preventing errors when one of the strings is empty
boolean az = a.length() == 0;
boolean bz = b.length() == 0;
if (az ^ bz) return NOT_EXIST;
// Need this next line if you want to interpret two empty strings as containing eachother
if (az && bz) return EXIST;
char start = b.charAt(0);
// This is known as a "label". Some say it's bad practice.
outer:
for (int i=0; i < a.length(); i++) {
if (a.charAt(i) == start) {
// Instead of using two methods, we can condense it like so
for (int q = 0; q < b.length(); q++) {
if ((i + q) >= a.length()) continue outer;
if (a.charAt(i + q) != b.charAt(q)) continue outer;
}
return EXIST;
}
}
return NOT_EXIST;
}
To find a substring "by hand", you need a nested loop; i.e. a loop inside a loop.
The outer loop tries all of the possible start positions for the substring in the string.
For a given start position, the inner loop tests all of the characters of the string that you are looking for against the string you are searching in.
In the naive substring search algorithm, the outer loop steps starts at index zero, and increments the index by one until it gets to the end of the string being searched. This can be improved on:
Every non-null string "contains" the empty string. It may be worth treating this as a special case.
It is easy to see that the outer loop can usually stop before the final. If you are searching for a string of length (say) 3, then the outer loop can stop at 3 from the end. (Think about it ....)
There are some clever algorithms which allow the outer loop to skip over some indexes. If you are interested, start by Googling for "Boyer-Moore string search".
(Note: the looping could be replaced with / written using recursion, but it is still there.)
Your code doesn't have a nested loops. By my reading, it is only going to find a match if the string you are searching for is at the start of the string you are searching. That is not correct.

How to improve efficiency

Write a function:
class Solution{
public int solution(int[] A);
}
that, given an array A of N integers, returns the smallest positive integer(greater than 0)
that does not occur in A.
For example, given A = [1,3,6,4,1,2], the function should return 5.
Given A = [1,2,3], the function should return 4.
Given A = [-1, -3], the function should return 1.
Write an efficient algorithm for the following assumptions.
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [-1,000,000..1,000,000].
I wrote the following algorithm in Java:
public class TestCodility {
public static void main(String args[]){
int a[] = {1,3,6,4,1,2};
//int a[] = {1,2,3};
//int b[] = {-1,-3};
int element = 0;
//checks if the array "a" was traversed until the last position
int countArrayLenght = 0;
loopExtern:
for(int i = 0; i < 1_000_000; i++){
element = i + 1;
countArrayLenght = 0;
loopIntern:
for(int j = 0; j < a.length; j++){
if(element == a[j]){
break loopIntern;
}
countArrayLenght++;
}
if(countArrayLenght == a.length && element > 0){
System.out.println("Smallest possible " + element);
break loopExtern;
}
}
}
}
It does the job but I am pretty sure that it is not efficient. So my question is, how to improve this algorithm so that it becomes efficient?
You should get a grasp on Big O, and runtime complexities.
Its a universal construct for better understanding the implementation of efficiency in code.
Check this website out, it shows the graph for runtime complexities in terms of Big O which can aid you in your search for more efficient programming.
http://bigocheatsheet.com/
However, long story short...
The least amount of operations and memory consumed by an arbitrary program is the most efficient way to achieve something you set out to do with your code.
You can make something more efficient by reducing redundancy in your algorithms and getting rid of any operation that does not need to occur to achieve what you are trying to do
Point is to sort your array and then iterate over it. With sorted array you can simply skip all negative numbers and then find minimal posible element that you need.
Here more general solution for your task:
import java.util.Arrays;
public class Main {
public static int solution(int[] A) {
int result = 1;
Arrays.sort(A);
for(int a: A) {
if(a > 0) {
if(result == a) {
result++;
} else if (result < a){
return result;
}
}
}
return result;
}
public static void main(String args[]){
int a[] = {1,3,6,4,1,2};
int b[] = {1,2,3};
int c[] = {-1,-3};
System.out.println("a) Smallest possible " + solution(a)); //prints 5
System.out.println("b) Smallest possible " + solution(b)); //prints 4
System.out.println("c) Smallest possible " + solution(c)); //prints 1
}
}
Complexity of that algorithm should be O(n*log(n))
The main idea is the same as Denis.
First sort, then process but using java8 feature.
There are few methods that may increase timings.(not very sure how efficient java 8 process them:filter,distinct and even take-while ... in the worst case you have here something similar with 3 full loops. One additional loop is for transforming array into stream). Overall you should get the same run-time complexity.
One advantage could be on verbosity, but also need some additional knowledge compared with Denis solution.
import java.util.function.Supplier;
import java.util.stream.IntStream;
public class AMin
{
public static void main(String args[])
{
int a[] = {-2,-3,1,2,3,-7,5,6};
int[] i = {1} ;
// get next integer starting from 1
Supplier<Integer> supplier = () -> i[0]++;
//1. transform array into specialized int-stream
//2. keep only positive numbers : filter
//3. keep no duplicates : distinct
//4. sort by natural order (ascending)
//5. get the maximum stream based on criteria(predicate) : longest consecutive numbers starting from 1
//6. get the number of elements from the longest "sub-stream" : count
long count = IntStream.of(a).filter(t->t>0).distinct().sorted().takeWhile(t->t== supplier.get()).count();
count = (count==0) ? 1 : ++count;
//print 4
System.out.println(count);
}
}
There are many solutions with O(n) space complexity and O(n) type complexity. You can convert array to;
set: array to set and for loop (1...N) check contains number or not. If not return number.
hashmap: array to map and for loop (1...N) check contains number or not. If not return number.
count array: convert given array to positive array count array like if arr[i] == 5, countArr[5]++, if arr[i] == 1, countArr[1]++ then check each item in countArr with for loop (1...N) whether greate than 1 or not. If not return it.
For now, looking more effective algoritm like #Ricola mentioned. Java solution with O(n) time complexity and O(1) space complexity:
static void swap(final int arr[], final int i,final int j){
final int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static boolean isIndexInSafeArea(final int arr[], final int i){
return arr[i] > 0 && arr[i] - 1 < arr.length && arr[i] != i + 1 ;
}
static int solution(final int arr[]){
for (int i = 0; i < arr.length; i++) {
while (isIndexInSafeArea(arr,i) && arr[i] != arr[arr[i] - 1]) {
swap(arr, i, arr[i] - 1);
}
}
for (int i = 0; i < arr.length; i++) {
if (arr[i] != i + 1) {
return i+1;
}
}
return arr.length + 1;
}

Check if string is a palindrome after removing one character. My working solution is too slow

I cant seem to find a proper solution to an exercise. The exercise asks to create a method that returns true if a string can be a palindrome by removing one character. I have a solution that works but fails tests of large (100,000 character) strings because its exceeding the time limit of 1 second. Can somebody point me in the right direction?
I realize my approach is brute force and I'm sure there's a better way to solve it. I'm assuming my problem lies with the iteration.
public class Main {
public static boolean makePalindrome(String mjono) {
StringBuilder sb = new StringBuilder(mjono);
for (int i = 0; i < mjono.length(); i++) {
sb.deleteCharAt(i);
if(isPalindrome(sb.toString())){
return true;
} else {
sb.insert(i, mjono.charAt(i));
}
}
return false;
}
private static boolean isPalindrome(String string) {
return string.equals(new StringBuilder(string).reverse().toString());
}
public static void main(String[] args) {
System.out.println(makePalindrome("ABCBXA"));
System.out.println(makePalindrome("ABCBAX"));
System.out.println(makePalindrome("ABCXBA"));
System.out.println(makePalindrome("ABCDE"));
System.out.println(makePalindrome("BAAAAC"));
}
}
These are the tests it fails:
#Test(timeout=1000)
public void suuri2() {
int n = 100000;
char[] t = new char[n];
for (int i = 0; i < n; i++) t[i] = 'A';
t[12345] = 'B';
testaaSuuri(new String(t), true);
}
#Test(timeout=1000)
public void suuri3() {
int n = 100000;
char[] t = new char[n];
for (int i = 0; i < n; i++) t[i] = 'A';
t[12345] = 'B';
t[54321] = 'C';
testaaSuuri(new String(t), false);
}
Thanks in advance.
Well, there's of course the naive solution running in O(n ^ 2) by trying each possibility to remove one char.
But we can certainly do better:
We can define a palindrome recursively:
palindrome = x.palindrome.x | x | x.x , where x is an arbitrary token
So how does that help us? Pretty simple: we can derive a rules that allow checking whether the string is palindromic in O(n).
A palindrome consists of a char c, followed by a string that must be empty or palindromic, followed by another c, if it's longer than 1 char. If it's of length 1, it's automatically palindromic.
Thus, the last character must be equal to the first, the second to the second to the last, and so on. So basically:
boolean isPalindrome(String s){
for(int i = 0 ; i < s.length() / 2 ; i++)
if(s.charAt(i) != s.charAt(s.length() - i - 1))
return false;
return true;
}
We have to alter this rule a bit, since once we may remove a single character. This introduces splitting the whole problem into two parts, as we can see from a definition:
palindrome_1 = s.x.palindrome.reverse(s) | s.palindrome.x.reverse(s) | palindrome
As we can easily see, this contains the original palindrome-definition, but in addition allows introduction of one additional char x.
static boolean isPalindrome_1(String s){
for(int i = 0 ; i < s.length() / 2 ; i++)
if(s.charAt(i) != s.charAt(s.length() - i - 1))
return isPalindrome(s , i + 1 , s.length() - i - 1) ||
isPalindrome(s , i , s.length() - i - 2);
return true;
}
static boolean isPalindrome(String s , int lower , int upper){
while(lower < upper){
if(s.charAt(lower) != s.charAt(upper))
return false;
lower++;
upper--;
}
return true;
}
An explanation/or at least an attempt to explain this:
This piece of code:
if(s.charAt(i) != s.charAt(s.length() - i - 1))
return isPalindrome(s , i + 1 , s.length() - i - 1) ||
isPalindrome(s , i , s.length() - i - 2);
Is required, if the definition of palindrome doesn't apply to our input-string. In that case, we have to check two possibilities, how the code was built:
s.x.palindrome.reverse(s)
s.palindrome.x.reverse(s)
If the definition of palindrome doesn't apply, we have reached a point, were we have to ommit either the character of at the start of the remaining string (x.palindrome) or the end of the remaining string (palindrome.x) and see, if the rest matches the definition of a palindrome. This is done by calling isPalindrome(...) with two different substrings, that are cut by one character at either the start or the end of the remaining string.
An few examples of how this code works:
A B C D E F E D C B A
| | portion that runs inside isPalindrome_1
A B D E F E D C B A
| | | | portion that can be checked inside isPalindrome_1
| | isPalindrome(s , i , s.length() - i - 2)
| | isPalindrome(s , i + 1 , s.length() - i - 1)
As we can see in the second example, the code searched for the first pair of chars that isn't equal. At this point, we have two substrings to search further, which each ommit one character, either at the beginning or the end of the string.
Efficiency:
This code runs in-place - there are never made any copies of the input-string. Runtime is O(n) (O(2 * n) to be precise). Building a faster solution won't be possible - at least until we get quantum computers ;)
Hint 1: since this is an exercise, posting solutions is inappropriate. (It detracts from the learning experience of doing the exercise yourself.)
Hint 2: The following operations are all O(N) for an N character String or StringBuilder:
Adding or removing a character from a StringBuilder
Creating a new StringBuilder from an existing StringBuilder
Reversing a StringBuilder
Creating a String from a StringBuilder (toString())
Comparing two equal or "almost equal" String objects.
(In most cases you copy or compare N characters. For insertion and deletion, you copy on average 0.5 N characters assuming that the buffer does not need to grow, but that is still O(N). For equals ... it is complicated, but the worst-case is clearly O(N).)
So a fast palindrome tester for large strings needs to avoid these operations.
Hint 3: you can treat the string as an array of characters, either by converting it into a char[] or using charAt(...).
Hint 4: you don't have to physically remove the char from the string. You can just get your algorithm to pretend it isn't there.
We can solve this using LCS(Longest Common Sub-sequence).
LCS tells us the length of the longest sub-sequence in two strings.
boolean isPalindromAfterRemovingOneChar(String s) {
int lengthOfLCS = lcs(s, s.reverse(), s.length());
return (s.length()- lengthOfLCS) == 1;
}
function test(s) {
const check = isPalindrome(s)
if (!check) {
const arr = s.split('')
const arrCheck = []
arr.forEach((element, i) => {
if (element !== arr[arr.length - i - 1]) {
const news = Array.from(arr)
console.log(arr, news.splice(i, 1))
isPalindrome(news.join(''))
}
});
console.log('arrCheck', arrCheck)
}
function isPalindrome(s) {
var reversedString = s.split("").reverse().join("");
if (s === reversedString) {
console.log('this string is palindrome', s)
return true
} else {
console.log('no')
return false
}
test('aaab')
Only need to compare the first half to the second half. Don't waste time reversing the whole String either.
private boolean isPalindrome(String string) {
char[] values = string.toCharArray();
for (int i = 0; i < values.length / 2; i++) {
if (values[i] != values[values.length - 1 - i])
return false;
}
return true;
}

Optimize Code for longest common sequence

I was trying to solve this practice problem, it is also quoted below.
The Chef is planning a buffet for the DirectiPlex inauguration party,
and everyone is invited. On their way in, each guest picks up a sheet
of paper containing a random number (this number may be repeated). The
guests then sit down on a round table with their friends.
The Chef now decides that he would like to play a game. He asks you to pick a random person from your table and have them read their
number out loud. Then, moving clockwise around the table, each person
will read out their number. The goal is to find that set of numbers
which forms an increasing subsequence. All people owning these
numbers will be eligible for a lucky draw! One of the software
developers is very excited about this prospect, and wants to maximize
the number of people who are eligible for the lucky draw. So, he
decides to write a program that decides who should read their number
first so as to maximize the number of people that are eligible for the
lucky draw. Can you beat him to it?
Input The first line contains t, the number of test cases (about 15). Then t test cases follow. Each test case consists of two
lines:
The first line contains a number N, the number of guests invited to
the party.
The second line contains N numbers a1, a2, ..., an separated by
spaces, which are the numbers written on the sheets of paper in
clockwise order.
Output For each test case, print a line containing a single number which is the maximum number of guests that can be eligible for
participating the the lucky draw.
Here's the solution that I have come up with
// http://www.codechef.com/problems/D2/
import java.io.*;
import java.util.*;
public class D2
{
public static void main(String [] args)
throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int numTestCases = Integer.parseInt(br.readLine());
for(int _t=0; _t<numTestCases; ++_t)
{
int N = Integer.parseInt(br.readLine());
StringTokenizer strtok = new StringTokenizer(br.readLine());
int [] originalArray = new int[N*2];
for(int i=0; i<N; ++i)
{
//this concatenates the array with itself at the time of reading the input itself
originalArray[i] = originalArray[N+i] = Integer.parseInt(strtok.nextToken());
}
//Now we calculate the length of the longest increasing sequence
int maxWinners = new LongestIncreasingSequence(originalArray).lengthOfLongestIncreasingSequence();
System.out.println(maxWinners);
}
}
}
class LongestIncreasingSequence
{
private int [] array;
private int [] longest;
private int subsequence_size;
public LongestIncreasingSequence(int [] A)
{
array = A;
longest = new int[array.length / 2];
longest[0] = array[0];
subsequence_size = 1;
}
public int lengthOfLongestIncreasingSequence()
{
for(int i=1; i<array.length; ++i)
{
if(array[i] < longest[0])
{
longest[0] = array[i];
}
else if(array[i] > longest[subsequence_size - 1])
{
longest[subsequence_size++] = array[i];
}
else
{
//Make the replacement with binary search
longest[getReplacementIndex(array[i])] = array[i];
}
}
return subsequence_size;
}
//Method to find the correct index using binary search
private int getReplacementIndex(int elem)
{
int left, right, mid;
left = 0; right = subsequence_size - 1;
while(right - left > 1)
{
mid = 1 + (right - left) / 2;
if(array[mid] >= elem)
{
if(mid != right) right = mid;
else --right;
}
else
{
left = mid;
}
}
return right;
}
}
The complexity is O(n(log(n)) I'm finding the Longest Increasing Sequence by concatenating the array with itself.
This however doesn't pass the time requirement, can someone help me speed up this implementation.
I would not do N rotations, but instead determine the longest (cyclic) run in one go. It is certainly doable, you just have to take care warping around at the end of the array.

Adaptation of LCS algorithm

new programmer here. I watched a video which displayed a recursive algorithm for LCS(longest common substring). The program only returned an int which was the length of the LCS between the two strings. I decided as an exercise to adapt the algorithm to return the string itself. Here is what I came up with, and it seems to be right, but I need to ask others more experienced if there are any bugs;
const int mAX=1001; //max size for the two strings to be compared
string soFar[mAX][mAX]; //keeps results of strings generated along way to solution
bool Get[mAX][mAX]; //marks what has been seen before(pairs of indexes)
class LCS{ //recursive version,use of global arrays not STL maps
private:
public:
string _getLCS(string s0,int k0, string s1,int k1){
if(k0<=0 || k1<=0){//base case
return "";
}
if(!Get[k0][k1]){ //checking bool memo to see if pair of indexes has been seen before
Get[k0][k1]=true; //mark seen pair of string indexs
if(s0[k0-1]==s1[k1-1]){
soFar[k0][k1]=s0[k0-1]+_getLCS(s0,k0-1,s1,k1-1);//if the char in positions k0 and k1 are equal add common char and move on
}
else{
string a=_getLCS(s0,k0-1,s1,k1);//this string is the result from keeping the k1 position the same and decrementing the k0 position
string b=_getLCS(s0,k0,s1,k1-1);//this string is the result from decrementing the k1 position keeping k0 the same
if(a.length()> b.length())soFar[k0][k1]=a;//the longer string is the one we are interested in
else
soFar[k0][k1]=b;
}
}
return soFar[k0][k1];
}
string LCSnum(string s0,string s1){
memset(Get,0,sizeof(Get));//memset works fine for zero, so no complaints please
string a=_getLCS(s0,s0.length(),s1,s1.length());
reverse(a.begin(),a.end());//because I start from the end of the strings, the result need to be reversed
return a;
}
};
I have only been programming for 6 months so I cant really tell if there is some bugs or cases where this algorithm will not work. It seems to work for two strings of size up to 1001 chars each.
What are the bugs and would the equivalent dynamic programming solution be faster or use less memory for the same result?
Thanks
Your program is not correct. What does it return for LCSnum("aba", "abba")?
string soFar[mAX][mAX] should be a hint that this is not a great solution. A simple dynamic programming solution (which has logic that you almost follow) has an array of size_t which is m*n in size, and no bool Get[mAX][mAX] either. (A better dynamic programming algorithm only has an array of 2*min(m, n).)
Edit: by the way, here is the space-efficient dynamic programming solution in Java. Complexity: time is O(m*n), space is O(min(m, n)), where m and n are the lengths of the strings. The result set is given in alphabetical order.
import java.util.Set;
import java.util.TreeSet;
class LCS {
public static void main(String... args) {
System.out.println(lcs(args[0], args[1]));
}
static Set<String> lcs(String s1, String s2) {
final Set<String> result = new TreeSet<String>();
final String shorter, longer;
if (s1.length() <= s2.length()) {
shorter = s1;
longer = s2;
}else{
shorter = s2;
longer = s1;
}
final int[][] table = new int[2][shorter.length()];
int maxLen = 0;
for (int i = 0; i < longer.length(); i++) {
int[] last = table[i % 2]; // alternate
int[] current = table[(i + 1) % 2];
for (int j = 0; j < shorter.length(); j++) {
if (longer.charAt(i) == shorter.charAt(j)) {
current[j] = (j > 0? last[j - 1] : 0) + 1;
if (current[j] > maxLen) {
maxLen = current[j];
result.clear();
}
if (current[j] == maxLen) {
result.add(shorter.substring(j + 1 - maxLen, j + 1));
}
}
}
}
return result;
}
}

Categories