Adaptation of LCS algorithm - java

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;
}
}

Related

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;
}

find number in close proximity to a given number

I have a number (int y = 12345). I want to find how I can shuffle y to find the number that is the middle of all possible combinations that can be made when shuffling. In this case, the answer would be 32541.
I initially tried to put 1,2,3,4,5 in a list and use Collections.shuffle to get all options and put them in a sortedSet. Then get the index at size()/2. But this doesn't work well for numbers larger than 123456789...
I also tried to use recursion to switch around all the numbers using heap's algorithm. That worked slightly better, but still couldn't process large numbers. See below. (I switched the integer to a string abcdefghij)
public static SortedSet<String> allStrings = new TreeSet<>();
public static SortedSet<String> findMidPerm(String strng) {
permutation("", strng);
return allStrings;
}
private static void permutation(String prefix, String str) {
int n = str.length();
if (n == 0) {
allStrings.add(prefix);
} else {
for (int i = 0; i < n; i++)
permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i + 1, n));
}
}
public static void main(String[] args) {
System.out.println(findMidPerm("abcdefghij"));
}
My current idea is to not create all possible numbers, but find the exact center of all possible combinations (int x = 33333). And then see which combination of numbers is closest to that number. In this case, this is either 32541 OR 34125. Both numbers are 792 steps away from x.
This is what I have so far:
public static float findMidPerm(String strng) {
float maxNum = findMaxNum(strng);
float minNum = findMinNum(strng);
float middleNum = findMiddleNum(minNum, maxNum);
return middleNum;
}
private static float findMiddleNum(float minNum, float maxNum) {
return (minNum+maxNum)/2;
}
private static float findMinNum(String strng) {
String s = "";
for (int i = 0; i <= strng.length(); i ++) {
s += i;
}
return Float.parseFloat(s);
}
private static Float findMaxNum(String strng) {
String s = "";
for (int i = strng.length(); i> 0; i --) {
s += i;
}
return Float.parseFloat(s);
}
public static void main(String[] args) {
System.out.println(findMidPerm("abcdefghijklmnop"));
}
Now for the difficult part of creating the algorithm that finds the order of integers closest to x. Does anyone have any ideas how this can be achieved?
(This is an answer to the original problem, how to find the median of all permutations, not for the XY-problem, how to find the permutation closest to a given number.)
I think, if you want to find exactly the median of the permutations, there is good and bad news: Good news: There seems to be an easy algorithm for that. Bad news: There is no exact median, as the number of permutations is always even (as it is 1 x 2 x 3 x ... x n)
Sort the input number so the digits are in ascending order
If the number has an odd number of digits, pick the middle digit as the first digit
The number now has an even number of digits; you have to pick either of the two middle digits, but this will skew the result (see the bad news above)
If you picked the lower of the middle digits, form the largest possible number from the remaining digits, otherwise the lowest possible number.
For your example: 12345 -> 3 1245 --> 32 145 --> 32541, or 12345 -> 3 1245 --> 34 125 --> 34125.
The intuition behind this is as follows: You can subdivide the n! (sorted) permutations of a number with n digits into n groups, each starting with the ith digit and having (n-1)! elements. As those groups are ordered, and each has the same number of elements, the median has to be in the middle group for an odd-numbered input, and right in between the middle two groups for an even-numbered input. So you have to pick either the largest of the smaller, or the smallest of the larger middle group. (And for an odd-numbered input, do the same for the n-1 sub-groups of the middle group.)
Here's a sample code (in Python, because I'm too lazy...)
# above algorithm
def med_perm(n):
lst = sorted(str(n)) # step 1
res = [lst.pop(len(lst)//2)] if len(lst) % 2 == 1 else [] # step 2
res.append(lst.pop(len(lst)//2)) # step 3
res.extend(lst) # step 4
return int(''.join(res))
# for reference
import itertools
def med_perm2(n):
perms = list(map(''.join, itertools.permutations(sorted(str(n)))))
return int(perms[len(perms)//2])
# testing
import random
n = random.randint(1, 100000000)
x, y = med_perm(n), med_perm2(n)
print(n, x, y, x==y)
I actually found a sneaky way to do this. I was writing everything down on paper and recognized a pattern. This is the first draft of the code and it can probably be done way more efficient. Feel free to adjust!
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Kata {
public static String findMidPerm(String strng) {
strng = sortString(strng);
StringBuilder sb = new StringBuilder();
List<Integer> s = createNum(strng);
for(int i =0; i <s.size(); i++) {
int b = s.get(i);
sb.append(strng.charAt(b-1));
}
return sb.toString();
}
private static String sortString(String strng) {
char[] ar = strng.toCharArray();
Arrays.sort(ar);
String sorted = String.valueOf(ar);
return sorted;
}
public static List<Integer> createNum(String strng) {
List<Integer> list = new ArrayList<>();
int s = strng.length() / 2;
int s2 = (strng.length() / 2) + 1;
if (strng.length() % 2 == 0) {
list.add(s);
for (int i = strng.length(); i > 0; i--)
if (i != s) {
list.add(i);
}
} else {
list.add(s2);
list.add(s);
for (int i = strng.length(); i > 0; i--)
if (i != s && i != s2) {
list.add(i);
}
}
return list;
}
public static void main(String[] args) {
System.out.println(findMidPerm("cmafilezoysht")); // cmafilezoysht is an input string in this case.
}
}

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

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;
}

Pair Palindrome

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).

Find all substrings that are palindromes

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

Categories