I'm new to Stack Overflow and I have a lab question for a programming class that's been eluding me. The problem requires us to shift the elements of a string s to the left k times. For instance, if the input is "Hello World" and 3, it would output "lo WorldHel"). It also has to work relatively efficiently for very large values of k. This is what I have so far:
String cyclicLeftShift(String s, int k){
String result="";
for(int i=0;i<k;i++){
result = s.substring(1, s.length() - 1) +s.charAt(0);
s=result;
}
return s;
}
My major issue is that the last character of the original string keeps getting overwritten by the subsequent iterations of the loop. I've tried a great number of permutations, including converting the whole thing to arrays (which violates the efficiency restriction in the original problem). I feel like there's just a tiny thing I'm not getting, and I was wondering if someone could give me a nudge in the right direction?
Thank you!
What you want is to split the string at position k and merge both parts together again but in reverse order.
The main problem is that k may be greater than or equal to the size of your string. So you need to bring k into a valid range again.
public static String cyclicLeftShift(String s, int k){
k = k%s.length();
return s.substring(k) + s.substring(0, k);
}
Testing the method:
public static void main(String[] args)
{
String test = "Hello World";
for(int i = 0; i < test.length()*3; i++)
System.out.println(cyclicLeftShift(test, i));
}
Output:
Hello World
ello WorldH
llo WorldHe
lo WorldHel
o WorldHell
WorldHello
WorldHello
orldHello W
rldHello Wo
ldHello Wor
dHello Worl
Hello World
ello WorldH
llo WorldHe
lo WorldHel
o WorldHell
WorldHello
WorldHello
orldHello W
rldHello Wo
ldHello Wor
dHello Worl
Hello World
ello WorldH
llo WorldHe
lo WorldHel
o WorldHell
WorldHello
WorldHello
orldHello W
rldHello Wo
ldHello Wor
dHello Worl
Try this one my boy:
String cyclicLeftShift(String s, int k) {
String result = s.substring(k);
for (int i = 0; i < k; i++) {
result += s.charAt(i);
}
return result;
}
Maybe I'm missing something, but can you not just mod k by the length of s to get n (number of characters to shift), then take the substring of [0,n) and append it to the substring [n, s.length() -1]?
e.g.:
String cyclicLeftShift(String s, int k){
String result="";
int n = k % s.length();
result = s.substring(n) + s.substring(0,n);
return result;
}
You can try this:
public static String cyclicLeftShift(String s, int k){
String result=s;
for(int i=0; i<k; i++){
result = result.substring(1) + result.charAt(0);
}
return result;
}
Here's an example via TutorialsPoint. Just click on compile then execute to see the result.
The arguments to String.substring() are (beginIndex, endIndex), NOT (beginIndex, count). You need to pass s.length() instead of s.length()-1... Or you could do it one of the much faster ways that others are posting
Not sure if this solution is of help ! But it worked for me :) Well you could try it out!
class Main {
public static void main(String[] args) {
System.out.println("Moving left by n characters");
String str1 = moveCHaracters("Hellow World", 4);
System.out.println(str1);
}
public static String moveCHaracters(String s, int k) {
String result = s.substring(s.length() - (k));
int length = s.length() - k;
if (k > 0) {
for (int i = 0; i < length; i++) {
result = result + s.charAt(i);
}
}
return result;
}
}
A quick solution, just need to take care that the val to be shifted must not be OutOfBounds.
String shiftLeft(String inp, int val)
{
String shifted_str="";
shifted_str=inp.substring(val);
shifted_str+=inp.substring(0,val);
return shifted_str;
}
Related
I have this code to find all pairs of string to form a palindrome. e.g) D: { AB, DEEDBA } => AB + DEEDBA -> YES and will be returned. Another example, { NONE, XENON } => NONE + XENON = > YES.
What would be running time of this ?
public static List<List<String>> pairPalindrome(List<String> D) {
List<List<String>> pairs = new LinkedList<>();
Set<String> set = new HashSet<>();
for (String s : D) {
set.add(s);
}
for (String s : D) {
String r = reverse(s);
for (int i = 0; i <= r.length(); i++) {
String prefix = r.substring(0, i);
if (set.contains(prefix)) {
String suffix = r.substring(i);
if (isPalindrom(suffix)) {
pairs.add(Arrays.asList(s, prefix));
}
}
}
}
return pairs;
}
private static boolean isPalindrom(String s) {
int i = 0;
int j = s.length() - 1;
char[] c = s.toCharArray();
while (i < j) {
if (c[i] != c[j]) {
return false;
}
i++;
j--;
}
return true;
}
private static String reverse(String s) {
char[] c = s.toCharArray();
int i = 0;
int j = c.length - 1;
while (i < j) {
char temp = c[i];
c[i] = c[j];
c[j] = temp;
i++;
j--;
}
return new String(c);
}
I'm going to take a few guesses here as I don't have much experience with Java.
First, isPalindrome is O(N) with the size of suffix string. Add operation to 'pairs' would probably be O(1).
Then, we have the for loop, it's O(N) with the length of r. Getting a substring I'd think is O(M) with the size of the substring. Checking if a hashmap contains a certain key, with a perfect hash function would be (IIRC) O(1), in your case we can assume O(lgN) (possibly). So, first for loop has O(NMlgK), where K is your hash table size, N is r's length and M is substring's length.
Finally we have the outmost for loop, it runs for each string in the string list, so that's O(N). Then, we reverse each of them. So for each of these strings we have another O(N) operation inside, with the other loop being O(NMlgK). So, overall complexity is O(L(N + NMlgK)), where L is the amount of strings you have. But, it'd reduce to O(LNMlgK). I'd like if someone verified or corrected my mistakes.
EDIT: Actually, substring length will at most be N, as the length of the entire string, so M is actually N. Now I'd probably say it's O(LNlgK).
If the input is 'abba' then the possible palindromes are a, b, b, a, bb, abba.
I understand that determining if string is palindrome is easy. It would be like:
public static boolean isPalindrome(String str) {
int len = str.length();
for(int i=0; i<len/2; i++) {
if(str.charAt(i)!=str.charAt(len-i-1) {
return false;
}
return true;
}
But what is the efficient way of finding palindrome substrings?
This can be done in O(n), using Manacher's algorithm.
The main idea is a combination of dynamic programming and (as others have said already) computing maximum length of palindrome with center in a given letter.
What we really want to calculate is radius of the longest palindrome, not the length.
The radius is simply length/2 or (length - 1)/2 (for odd-length palindromes).
After computing palindrome radius pr at given position i we use already computed radiuses to find palindromes in range [i - pr ; i]. This lets us (because palindromes are, well, palindromes) skip further computation of radiuses for range [i ; i + pr].
While we search in range [i - pr ; i], there are four basic cases for each position i - k (where k is in 1,2,... pr):
no palindrome (radius = 0) at i - k
(this means radius = 0 at i + k, too)
inner palindrome, which means it fits in range
(this means radius at i + k is the same as at i - k)
outer palindrome, which means it doesn't fit in range
(this means radius at i + k is cut down to fit in range, i.e because i + k + radius > i + pr we reduce radius to pr - k)
sticky palindrome, which means i + k + radius = i + pr
(in that case we need to search for potentially bigger radius at i + k)
Full, detailed explanation would be rather long. What about some code samples? :)
I've found C++ implementation of this algorithm by Polish teacher, mgr Jerzy Wałaszek.
I've translated comments to english, added some other comments and simplified it a bit to be easier to catch the main part.
Take a look here.
Note: in case of problems understanding why this is O(n), try to look this way:
after finding radius (let's call it r) at some position, we need to iterate over r elements back, but as a result we can skip computation for r elements forward. Therefore, total number of iterated elements stays the same.
Perhaps you could iterate across potential middle character (odd length palindromes) and middle points between characters (even length palindromes) and extend each until you cannot get any further (next left and right characters don't match).
That would save a lot of computation when there are no many palidromes in the string. In such case the cost would be O(n) for sparse palidrome strings.
For palindrome dense inputs it would be O(n^2) as each position cannot be extended more than the length of the array / 2. Obviously this is even less towards the ends of the array.
public Set<String> palindromes(final String input) {
final Set<String> result = new HashSet<>();
for (int i = 0; i < input.length(); i++) {
// expanding even length palindromes:
expandPalindromes(result,input,i,i+1);
// expanding odd length palindromes:
expandPalindromes(result,input,i,i);
}
return result;
}
public void expandPalindromes(final Set<String> result, final String s, int i, int j) {
while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) {
result.add(s.substring(i,j+1));
i--; j++;
}
}
So, each distinct letter is already a palindrome - so you already have N + 1 palindromes, where N is the number of distinct letters (plus empty string). You can do that in single run - O(N).
Now, for non-trivial palindromes, you can test each point of your string to be a center of potential palindrome - grow in both directions - something that Valentin Ruano suggested.
This solution will take O(N^2) since each test is O(N) and number of possible "centers" is also O(N) - the center is either a letter or space between two letters, again as in Valentin's solution.
Note, there is also O(N) solution to your problem, based on Manacher's algoritm (article describes "longest palindrome", but algorithm could be used to count all of them)
I just came up with my own logic which helps to solve this problem.
Happy coding.. :-)
System.out.println("Finding all palindromes in a given string : ");
subPal("abcacbbbca");
private static void subPal(String str) {
String s1 = "";
int N = str.length(), count = 0;
Set<String> palindromeArray = new HashSet<String>();
System.out.println("Given string : " + str);
System.out.println("******** Ignoring single character as substring palindrome");
for (int i = 2; i <= N; i++) {
for (int j = 0; j <= N; j++) {
int k = i + j - 1;
if (k >= N)
continue;
s1 = str.substring(j, i + j);
if (s1.equals(new StringBuilder(s1).reverse().toString())) {
palindromeArray.add(s1);
}
}
}
System.out.println(palindromeArray);
for (String s : palindromeArray)
System.out.println(s + " - is a palindrome string.");
System.out.println("The no.of substring that are palindrome : "
+ palindromeArray.size());
}
Output:-
Finding all palindromes in a given string :
Given string : abcacbbbca
******** Ignoring single character as substring palindrome ********
[cac, acbbbca, cbbbc, bb, bcacb, bbb]
cac - is a palindrome string.
acbbbca - is a palindrome string.
cbbbc - is a palindrome string.
bb - is a palindrome string.
bcacb - is a palindrome string.
bbb - is a palindrome string.
The no.of substring that are palindrome : 6
I suggest building up from a base case and expanding until you have all of the palindomes.
There are two types of palindromes: even numbered and odd-numbered. I haven't figured out how to handle both in the same way so I'll break it up.
1) Add all single letters
2) With this list you have all of the starting points for your palindromes. Run each both of these for each index in the string (or 1 -> length-1 because you need at least 2 length):
findAllEvenFrom(int index){
int i=0;
while(true) {
//check if index-i and index+i+1 is within string bounds
if(str.charAt(index-i) != str.charAt(index+i+1))
return; // Here we found out that this index isn't a center for palindromes of >=i size, so we can give up
outputList.add(str.substring(index-i, index+i+1));
i++;
}
}
//Odd looks about the same, but with a change in the bounds.
findAllOddFrom(int index){
int i=0;
while(true) {
//check if index-i and index+i+1 is within string bounds
if(str.charAt(index-i-1) != str.charAt(index+i+1))
return;
outputList.add(str.substring(index-i-1, index+i+1));
i++;
}
}
I'm not sure if this helps the Big-O for your runtime, but it should be much more efficient than trying each substring. Worst case would be a string of all the same letter which may be worse than the "find every substring" plan, but with most inputs it will cut out most substrings because you can stop looking at one once you realize it's not the center of a palindrome.
I tried the following code and its working well for the cases
Also it handles individual characters too
Few of the cases which passed:
abaaa --> [aba, aaa, b, a, aa]
geek --> [g, e, ee, k]
abbaca --> [b, c, a, abba, bb, aca]
abaaba -->[aba, b, abaaba, a, baab, aa]
abababa -->[aba, babab, b, a, ababa, abababa, bab]
forgeeksskeegfor --> [f, g, e, ee, s, r, eksske, geeksskeeg,
o, eeksskee, ss, k, kssk]
Code
static Set<String> set = new HashSet<String>();
static String DIV = "|";
public static void main(String[] args) {
String str = "abababa";
String ext = getExtendedString(str);
// will check for even length palindromes
for(int i=2; i<ext.length()-1; i+=2) {
addPalindromes(i, 1, ext);
}
// will check for odd length palindromes including individual characters
for(int i=1; i<=ext.length()-2; i+=2) {
addPalindromes(i, 0, ext);
}
System.out.println(set);
}
/*
* Generates extended string, with dividors applied
* eg: input = abca
* output = |a|b|c|a|
*/
static String getExtendedString(String str) {
StringBuilder builder = new StringBuilder();
builder.append(DIV);
for(int i=0; i< str.length(); i++) {
builder.append(str.charAt(i));
builder.append(DIV);
}
String ext = builder.toString();
return ext;
}
/*
* Recursive matcher
* If match is found for palindrome ie char[mid-offset] = char[mid+ offset]
* Calculate further with offset+=2
*
*
*/
static void addPalindromes(int mid, int offset, String ext) {
// boundary checks
if(mid - offset <0 || mid + offset > ext.length()-1) {
return;
}
if (ext.charAt(mid-offset) == ext.charAt(mid+offset)) {
set.add(ext.substring(mid-offset, mid+offset+1).replace(DIV, ""));
addPalindromes(mid, offset+2, ext);
}
}
Hope its fine
public class PolindromeMyLogic {
static int polindromeCount = 0;
private static HashMap<Character, List<Integer>> findCharAndOccurance(
char[] charArray) {
HashMap<Character, List<Integer>> map = new HashMap<Character, List<Integer>>();
for (int i = 0; i < charArray.length; i++) {
char c = charArray[i];
if (map.containsKey(c)) {
List list = map.get(c);
list.add(i);
} else {
List list = new ArrayList<Integer>();
list.add(i);
map.put(c, list);
}
}
return map;
}
private static void countPolindromeByPositions(char[] charArray,
HashMap<Character, List<Integer>> map) {
map.forEach((character, list) -> {
int n = list.size();
if (n > 1) {
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (list.get(i) + 1 == list.get(j)
|| list.get(i) + 2 == list.get(j)) {
polindromeCount++;
} else {
char[] temp = new char[(list.get(j) - list.get(i))
+ 1];
int jj = 0;
for (int ii = list.get(i); ii <= list
.get(j); ii++) {
temp[jj] = charArray[ii];
jj++;
}
if (isPolindrome(temp))
polindromeCount++;
}
}
}
}
});
}
private static boolean isPolindrome(char[] charArray) {
int n = charArray.length;
char[] temp = new char[n];
int j = 0;
for (int i = (n - 1); i >= 0; i--) {
temp[j] = charArray[i];
j++;
}
if (Arrays.equals(charArray, temp))
return true;
else
return false;
}
public static void main(String[] args) {
String str = "MADAM";
char[] charArray = str.toCharArray();
countPolindromeByPositions(charArray, findCharAndOccurance(charArray));
System.out.println(polindromeCount);
}
}
Try out this. Its my own solution.
// Maintain an Set of palindromes so that we get distinct elements at the end
// Add each char to set. Also treat that char as middle point and traverse through string to check equality of left and right char
static int palindrome(String str) {
Set<String> distinctPln = new HashSet<String>();
for (int i=0; i<str.length();i++) {
distinctPln.add(String.valueOf(str.charAt(i)));
for (int j=i-1, k=i+1; j>=0 && k<str.length(); j--, k++) {
// String of lenght 2 as palindrome
if ( (new Character(str.charAt(i))).equals(new Character(str.charAt(j)))) {
distinctPln.add(str.substring(j,i+1));
}
// String of lenght 2 as palindrome
if ( (new Character(str.charAt(i))).equals(new Character(str.charAt(k)))) {
distinctPln.add(str.substring(i,k+1));
}
if ( (new Character(str.charAt(j))).equals(new Character(str.charAt(k)))) {
distinctPln.add(str.substring(j,k+1));
} else {
continue;
}
}
}
Iterator<String> distinctPlnItr = distinctPln.iterator();
while ( distinctPlnItr.hasNext()) {
System.out.print(distinctPlnItr.next()+ ",");
}
return distinctPln.size();
}
Code is to find all distinct substrings which are palindrome.
Here is the code I tried. It is working fine.
import java.util.HashSet;
import java.util.Set;
public class SubstringPalindrome {
public static void main(String[] args) {
String s = "abba";
checkPalindrome(s);
}
public static int checkPalindrome(String s) {
int L = s.length();
int counter =0;
long startTime = System.currentTimeMillis();
Set<String> hs = new HashSet<String>();
// add elements to the hash set
System.out.println("Possible substrings: ");
for (int i = 0; i < L; ++i) {
for (int j = 0; j < (L - i); ++j) {
String subs = s.substring(j, i + j + 1);
counter++;
System.out.println(subs);
if(isPalindrome(subs))
hs.add(subs);
}
}
System.out.println("Total possible substrings are "+counter);
System.out.println("Total palindromic substrings are "+hs.size());
System.out.println("Possible palindromic substrings: "+hs.toString());
long endTime = System.currentTimeMillis();
System.out.println("It took " + (endTime - startTime) + " milliseconds");
return hs.size();
}
public static boolean isPalindrome(String s) {
if(s.length() == 0 || s.length() ==1)
return true;
if(s.charAt(0) == s.charAt(s.length()-1))
return isPalindrome(s.substring(1, s.length()-1));
return false;
}
}
OUTPUT:
Possible substrings:
a
b
b
a
ab
bb
ba
abb
bba
abba
Total possible substrings are 10
Total palindromic substrings are 4
Possible palindromic substrings: [bb, a, b, abba]
It took 1 milliseconds
I need to know how many chars of the same type are in one string.
I have tried this
String x ="(3+3)*(4-2)";
int a = x.indexOf( "(" );
But that only give me the first index
You can use a loop and use the other method indexOf(int, int):
String x ="(3+3)*(4-2)";
int a = x.indexOf( "(" );
while (a >= 0) {
System.out.println("Char '(' found at: "+a);
a = x.indexOf('(', a+1);
}
It seems like it would be better to put it in a separate function:
// accepts a string and a char to find the number of occurrences of
public static int get_count(String s, char c) {
int count = 0; // count initially 0
for (int i = 0; i < s.length(); i++) // loop through the whole string
if (s.charAt(i) == c)
count ++; // increment every time an occurrence happens
return count; // return the count in the end
}
You can call it like this:
System.out.println(get_count("(3+3)*(4-2)", '('));
// Output: 2
There's a few ways I could think of doing this, but one of the simplest would be to simply loop the through characters in the String....
String x ="(3+3)*(4-2)";
int count = 0;
for (char c : x.toCharArray()) {
if (c == '(') {
count++;
}
}
System.out.println(count);
And just because it can be done...you could use a little regexp...(I know, overkill)
Pattern p = Pattern.compile("\\(");
Matcher matcher = p.matcher(x);
while (matcher.find()) {
count++;
}
System.out.println(count);
The code below does what you want. If performance is critical you can make optimization with this. If you want more elegant solutions you may take a look at regex library of java.
int occurences = 0;
String x ="(3+3)*(4-2)";
char tolookfor = '(';
for(int i = 0; i < x.length() ; i++)
{
if(x.charAt(i) == tolookfor)
occurences++;
}
You can try this
String x ="(3+3)*(4-2)";
char[] arr=x.toCharArray();
Map<String,Integer> map=new HashMap<>();
for(int i=0;i<arr.length;i++){
Integer upTo=map.get(String.valueOf(arr[i]));
if (upTo==null) {
upTo=0;
}
map.put(String.valueOf(arr[i]),upTo+1) ;
}
for (Map.Entry<String,Integer> entry:map.entrySet()){
System.out.println("Number of "+entry.getKey()+" in this string is: "+entry.getValue());
}
out put
Number of 3 in this string is: 2
Number of 2 in this string is: 1
Number of 4 in this string is: 1
Number of * in this string is: 1
Number of + in this string is: 1
Number of ( in this string is: 2
Number of ) in this string is: 2
Number of - in this string is: 1
It’s unbelievable how complicated the answers to such a simple question can be.
x.indexOf( "(" );
But that only give me the first index
Use x.indexOf( "(", fromIndex ); to find more occurrences. Point.
By the way, if you are looking for a single char you can use x.indexOf( '('); and x.indexOf( '(', fromIndex ); to be more efficient.
So the most efficient way without reinventing the wheel would be:
int count=0;
for(int pos=s.indexOf('('); pos!=-1; pos=s.indexOf('(', pos+1)) count++;
Use StringUtils.countMatches
StringUtils.countMatches(value,"(");
or
public static int countMatches(String value, String valueToCount) {
if (value.isEmpty() || valueToCount.isEmpty()) {
return 0;
}
int count = 0;
int index = 0;
while ((index = value.indexOf(valueToCount, index)) != -1) {
count++;
index += valueToCount.length();
}
return count;
}
This will help you!
public static int counter(String x, char y) {
char[] array=x.toCharArray();
int count=0;
for(int i=0;i<x.length();i++)
{
if(y==array[i]) count++;
}
return (count>0)? count:0;
}
I am trying to figure out how to split a string into an 2d array based on k.
I know how to split it into individual strings or by regex.
How do you go about splitting it like this;
String text = "thisismyp";
// Result (where k = 3):
char[][] = {
{'t','h','i'},
{'s','i','s'},
{'m','y','p'}
};
.{k} should work where you have to compose the regex yourself inserting the value of k into it.
The regex is:
(.{3})
Nice and simple. You can add case insensitivity, newline handling, etc, according to your use case.
I haven't tested it, so maybe you need to change a little bit, but I think that this will work:
public String[] split(String str, int k)
{
String[] a = new String[Math.ceil((double)str.length() / (double)k)];
for (int i = 0; i < str.length(); i += k)
{
a[i / k] = str.substring(i, Math.min(i + k, str.length()));
}
return a;
}
If you want it really as a char matrix, use something like this:
public char[][] split(String str, int k)
{
char[][] a = new char[Math.ceil((double)str.length() / (double)k)][0];
for (int i = 0; i < str.length(); i += k)
{
String part = str.substring(i, Math.min(i + k, str.length()));
a[i / k] = part.toCharArray();
}
return a;
}
If you don't want to use a regex you can brute-force it. Brute force is faster but in your case I wouldn't worry about performance. Still if anyone lands on this page and is not using a language with easy regex support; here is the fast Java implementation, embedded within a complete application you can compile and run.
import java.util.Arrays;
public class Split {
private static int K = 3;
/**
* Splits a string into a 2-D array of chars. An array or list of strings would
* be better, but <strong>the OP asked for an array of array of chars!!!
*/
public static char[][] split(String s) {
char[][] result = new char[(s.length() + (K-1)) / K][K];
int row = 0, col = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
result[row][col] = c;
if (col == K - 1) {row++; col = 0;} else col++;
}
return result;
}
// Sorry about the hideous copy/paste
public static void main(String[] args) {
System.out.println(Arrays.deepToString(split("")));
System.out.println(Arrays.deepToString(split("1")));
System.out.println(Arrays.deepToString(split("12")));
System.out.println(Arrays.deepToString(split("123")));
System.out.println(Arrays.deepToString(split("1234")));
System.out.println(Arrays.deepToString(split("12345")));
System.out.println(Arrays.deepToString(split("123456")));
System.out.println(Arrays.deepToString(split("1234567")));
System.out.println(Arrays.deepToString(split("12345678")));
System.out.println(Arrays.deepToString(split("123456789")));
System.out.println(Arrays.deepToString(split("1234567890")));
}
}
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;
}
}