Related
I would like to verify the space and time complexity of my solution to subsets problem on leetcode. The space complexity is O(N) due to stack space. The time complexity is O(2^N) as the work on each ith level is adding 2^i elements to the list. So summing 2^i from 0 to N yields O(2^N). Am I correct? I am not sure because the 3 official solutions have time complexity O(N*2^N).
import java.util.ArrayList;
import java.util.List;
public class Solution {
// Space Complexity: O(N)
// Time Complexity: 2^0 + 2^1 + ... + 2^N = O(2^N)
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> subsets = new ArrayList<>();
subsets.add(List.of());
subsetsHelper(nums, 0, subsets);
return subsets;
}
private void subsetsHelper(int[] nums, int index, List<List<Integer>> subsets) {
if (index >= nums.length) return;
int current = nums[index];
int initialSize = subsets.size();
for (int i = 0; i < initialSize; i++) {
var list = subsets.get(i);
var listCopy = new ArrayList<>(list);
listCopy.add(current);
subsets.add(listCopy);
}
subsetsHelper(nums, index + 1, subsets);
}
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println(solution.subsets(new int[]{0, 1, 2}));
}
}
I figured out why it's 2^N times N.
For subsetsHelper(), at ith level, the number of subsets is 2^(i-1). And the size of each subsetList in it is O(N). As I double the size of each sub subsetList (from list to listCopy in code) in subsetsHelper() its time complexity is O(N*2^i).
And since the function is called N times, the time complexity of subsets() is O(N*(2^0 +...+2^(N-1)) = O(N*2^N)
Let us consider the implementation of Fibonacci series using dynamic programming.
// Fibonacci Series using Dynamic Programming
class fibonacci
{
static int fib(int n)
{
/* Declare an array to store Fibonacci numbers. */
int f[] = new int[n+1];
int i;
/* 0th and 1st number of the series are 0 and 1*/
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
{
/* Add the previous 2 numbers in the series
and store it */
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
public static void main (String args[])
{
int n = 9;
System.out.println(fib(n));
}
}
We use the dynamic programming so that the repetition of the recursive work does not occur. But here when every time the function has been called,a new array will be generated. So how could this algorithm is said to be more optimized ?
one optimization would be only save the last 2 values instead of all results. You don't need to store all your results.
you also can write the fibonacci series recursively in O(n):
int fib(int n1, int n2, int counter)
{
if(counter == 0)
{
return n2;
}
else
{
return fib(n2,n2 + n1,counter-1);
}
}
//to start:
int result = fib(0,1,100); //gives you the 100 fibonacci value
This code runs recursively and is easy to read. You don't have to initialize an array or other stuff.
alternatively you can use the nonrecursive option:
int fib(int number)
{
int n1 = 0;
int n2 = 1;
int temp;
for(int i = 0; i< number;i++)
{
temp = n1 + n2;
n1 = n2;
n2 = temp;
}
return n2;
}
If you want to store your results, you have to initialize the array outside of your fib function:
// Fibonacci Series using Dynamic Programming
class fibonacci
{
/* Declare an array to store Fibonacci numbers. */
int f[];
static void init(int n)
{ /* 0th and 1st number of the series are 0 and 1*/
f = new int[n+1];
f[0] = 0;
f[1] = 1;
}
static int fib(int n)
{
int i;
for (i = 2; i <= n; i++)
{
/* Add the previous 2 numbers in the series
and store it */
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
public static void main (String args[])
{
int n = 9;
init(n);
System.out.println(fib(n));
}
}
We use Memoization to prevent further recursion with Dynamic Programming. Memoization is the way to store already calculated values ex - let say fib(m) for index m is calculated and stored in memoization table. Further in upcoming recursions if fib(m) reoccur for calculation, then we take it from memoization table instead of doing again fib(m-1)+fib(m-2) i.e. avoid unnecessary recursions...Note - this will keep the complexity linear i.e. O(n).
We can implement this memoization in many ways for faster search. However here for Fibonacci we can implement memoization as array. And yes memoization table will be initialized just once. Below code is illustration for above explanation -
Note - we have taken a variable "complexity" which will incremented whenever we cant find value in memoization table i.e. whenever we go for recursion..
package com.company.dynamicProgramming;
import java.math.BigInteger;
public class FibonacciByBigDecimal {
static int complexity = 0;
public static void main(String ...args) {
int n = 200;
BigInteger[] memoization = new BigInteger[n + 1];
System.out.println("fibonacci of "+ n + " is : " + fibByDivCon(n, memoization));
System.out.println("Complexity is "+complexity);
}
static BigInteger fibByDivCon(int n, BigInteger[] memoization){
if(memoization[n]!=null){
return memoization[n];
}
complexity++;
if (n == 1 || n== 2){
memoization[n] = BigInteger.ONE;
return BigInteger.ONE;
}
// creates 2 further entries in stack
BigInteger fibOfn = fibByDivCon(n-1, memoization).add( fibByDivCon(n-2, memoization)) ;
memoization[n] = fibOfn;
return fibOfn;
}
}
Above i am calculating Fibonacci number at index 200. When I ran above code the result is : -
fibonacci of 200 is : 280571172992510140037611932413038677189525
Complexity is 200
Process finished with exit code 0
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
This is a problem based on finding size of maximal nested intervals set..
There are many intervals each defined by pair containing a starting point and a ending point (si, ei). Two intervals i1 and i2 are called nested if i2 lies completely inside of i1. Example:- (2,6) and (3,4) are nested, since (3,4) is a part of (2,6). Similarly k intervals i1, i2, i3....ik are called nested if, i2 lies inside i1, i3 lies inside i2, ...and so on. Determine size of largest set of intervals from the given intervals, such that all the intervals in that set could produce a nesting.
I thought it in following way:- Let us take an e.g.- (0,7) (0,5) (1,21) (1,9) (2,8) (2,4) (3,20) (4,16) (5,15) (6,21) Sort it in the way such that a[i-1]<=a[i] && b[i-1]>=b[i] Than starting from first interval we start a linked list.. if the next interval comes inside a interval we move down the node and traverse the graph created down the node(other than main list)..we store the pointer of the maximum level node in this graph at which the new interval can fit.. and traverse further in linked list to see under whom the current interval comes.. at last we have a pointer to a node with whom we have to attach the current interval.. and compare the level of this node with the maximum level we already have..... The final value of maximum level is size of maximal nested intervals set..
complexity of above solution is likely to be:- O(n(k+l) + nlogn)
I guess it is difficult to get it this way, but I have no other option... if anyone got any other algorithm to solve it.. please post because my algorithm will take much longer to implement(lot of data structures involved)... thanks!!!
Edit: A few solutions to the problem are posted here, including two that claim to be O(n lg n). However, I don't think the O(n lg n) solutions work. I made comments on that page indicating why. If anyone has an O(n lg n) solution, I'd love to hear it.
Quadratic Solution
This problem can be solved in O(n^2) time using dynamic programming:
Compute how many intervals each interval contains (can be done with two nested loops)
Sort the intervals in ascending order of number of contained intervals
Use the recurrence MaxNestedIntervals to solve the problem
*Note:
Step 1 can be done in O(n lg n) time using the solution here: Sub O(n^2) algorithm for counting nested intervals?
Step 2 can be done in O(n lg n) time with any optimal comparison-based sorting algorithm.
There may be a way to optimize step 3, but I haven't found it yet.
Recurrence
MaxNestedIntervals(i) =
max {j = 0 to i-1} :
1 + MaxNestedIntervals(j) if interval i contains interval j
0 if interval i doesn't contain interval j
Base case
MaxNestedIntervals(i) =
0 if interval i contains 0 intervals
1 if interval i contains 1 interval
Sample code
import java.util.*;
public class MaxNestedIntervals {
public static void main(String[] args) {
Interval[] intervals = new Interval[10];
intervals[0] = new Interval(0, 7);
intervals[1] = new Interval(0, 5);
intervals[2] = new Interval(1, 21);
intervals[3] = new Interval(1, 9);
intervals[4] = new Interval(2, 8);
intervals[5] = new Interval(2, 4);
intervals[6] = new Interval(3, 20);
intervals[7] = new Interval(4,16);
intervals[8] = new Interval(5,15);
intervals[9] = new Interval(6,21);
int n = intervals.length;
AugmentedInterval[] augmentedIntervals = new AugmentedInterval[n];
for (int i = 0; i < n; i++) {
augmentedIntervals[i] = new AugmentedInterval(intervals[i]);
}
for (int i = 0; i < n; i++) {
AugmentedInterval outerInterval = augmentedIntervals[i];
for (int j = 0; j < n; j++) {
if (i == j) {
continue;
}
AugmentedInterval innerInterval = augmentedIntervals[j];
if (outerInterval.contains(innerInterval)) {
outerInterval.numContainedIntervals++;
if (outerInterval.childInterval == null) {
outerInterval.childInterval = innerInterval;
}
}
}
}
Arrays.sort(augmentedIntervals, new Comparator<AugmentedInterval>() {
public int compare(AugmentedInterval i, AugmentedInterval j) {
return i.numContainedIntervals - j.numContainedIntervals;
}
});
int maxNestedIntervals = 0;
AugmentedInterval parentInterval = null;
for (int i = 0; i < n; i++) {
AugmentedInterval currentInterval = augmentedIntervals[i];
if (currentInterval.numContainedIntervals == 0) {
currentInterval.maxNestedIntervals = 0;
} else if (currentInterval.numContainedIntervals == 1) {
currentInterval.maxNestedIntervals = 1;
} else {
int maxNestedIntervalsForCurrentInterval = 0;
for (int j = 0; j < i; j++) {
AugmentedInterval candidateNestedInterval = augmentedIntervals[j];
int maxNestedIntervalsForCurrentCandidate = candidateNestedInterval.maxNestedIntervals + 1;
if (currentInterval.contains(candidateNestedInterval) && maxNestedIntervalsForCurrentCandidate >= maxNestedIntervalsForCurrentInterval) {
maxNestedIntervalsForCurrentInterval = maxNestedIntervalsForCurrentCandidate;
currentInterval.childInterval = candidateNestedInterval;
}
}
currentInterval.maxNestedIntervals = maxNestedIntervalsForCurrentInterval;
if (maxNestedIntervalsForCurrentInterval >= maxNestedIntervals) {
maxNestedIntervals = maxNestedIntervalsForCurrentInterval;
parentInterval = currentInterval;
}
}
}
if (n == 0) {
System.out.println("The largest set of nested intervals is the empty set.");
} else if (maxNestedIntervals == 0) {
System.out.println("The largest set of nested intervals has 1 interval.\n");
System.out.println("That interval is:");
} else {
System.out.println("The largest set of nested intervals has " + (maxNestedIntervals + 1) + " intervals.\n");
System.out.println("Those intervals are:");
}
for (AugmentedInterval currentInterval = parentInterval; currentInterval != null; currentInterval = currentInterval.childInterval) {
System.out.println(currentInterval);
}
System.out.println();
}
private static class Interval implements Comparable<Interval> {
public int start = 0;
public int end = 0;
public Interval(int start, int end) {
this.start = start;
this.end = end;
}
public int size() {
return this.end - this.start;
}
public boolean contains(Interval other) {
return (this.start <= other.start && this.end >= other.end);
}
public int compareTo(Interval other) {
return this.size() - other.size();
}
public String toString() {
return "[" + this.start + ", " + this.end + "]";
}
}
private static class AugmentedInterval extends Interval {
public int numContainedIntervals = 0;
public int maxNestedIntervals = 0;
public AugmentedInterval childInterval = null;
public AugmentedInterval(Interval interval) {
super(interval.start, interval.end);
}
}
}
Here is the problem (6.7 ch6 ) from Algorithms book (by Vazirani) that slightly differs from the classical problem that finding longest palindrome. How can I solve this problem ?
A subsequence is palindromic if it is
the same whether read left to right or
right to left. For instance, the
sequence
A,C,G,T,G,T,C,A,A,A,A,T,C,G
has many palindromic subsequences,
including A,C,G,C,A and A,A,A,A
(on the other hand, the subsequence
A,C,T is not palindromic). Devise an
algorithm that takes a sequence x[1
...n] and returns the (length of the)
longest palindromic subsequence. Its
running time should be O(n^2)
This can be solved in O(n^2) using dynamic programming. Basically, the problem is about building the longest palindromic subsequence in x[i...j] using the longest subsequence for x[i+1...j], x[i,...j-1] and x[i+1,...,j-1] (if first and last letters are the same).
Firstly, the empty string and a single character string is trivially a palindrome.
Notice that for a substring x[i,...,j], if x[i]==x[j], we can say that the length of the longest palindrome is the longest palindrome over x[i+1,...,j-1]+2. If they don't match, the longest palindrome is the maximum of that of x[i+1,...,j] and y[i,...,j-1].
This gives us the function:
longest(i,j)= j-i+1 if j-i<=0,
2+longest(i+1,j-1) if x[i]==x[j]
max(longest(i+1,j),longest(i,j-1)) otherwise
You can simply implement a memoized version of that function, or code a table of longest[i][j] bottom up.
This gives you only the length of the longest subsequence, not the actual subsequence itself. But it can easily be extended to do that as well.
This problem can also be done as a variation of a very common problem called the LCS(Longest Common sub sequence) problem.
Let the input string be represented by a character array s1[0...n-1].
1) Reverse the given sequence and store the reverse in another array say s2[0..n-1] which in essence is s1[n-1....0]
2) LCS of the given sequence s1 and reverse sequence s2 will be the longest palindromic sequence.
This solution is also a O(n^2) solution.
It makes me a little confused that the difference between substring and subsequence.(See Ex6.8 and 6.11) According to our comprehension of subsequence, the giving example doesn't have the palindromic subsequence ACGCA.
Here's my pseudo code, I'm not quite sure about the initialization ><
for i = 1 to n do
for j = 1 to i-1 do
L(i,j) = 0
for i = 1 to n do
L(i,i) = 1
for i = n-1 to 1 do //pay attention to the order when filling the table
for j = i+1 to n do
if x[i] = x[j] then
L(i,j) = 2 + L(i+1, j-1)
else do
L(i,j) = max{L(i+1, j), L(i, j-1)}
return max L(i,j)
preparing for the algorithm final...
Working Java Implementation of Longest Palindrome Sequence
public class LongestPalindrome
{
int max(int x , int y)
{
return (x>y)? x:y;
}
int lps(char[] a ,int i , int j)
{
if(i==j) //If only 1 letter
{
return 1;
}
if(a[i] == a[j] && (i+1) == j) // if there are 2 character and both are equal
{
return 2;
}
if(a[i] == a[j]) // If first and last char are equal
{
return lps(a , i+1 , j-1) +2;
}
return max(lps(a,i+1 ,j),lps(a,i,j-1));
}
public static void main(String[] args)
{
String s = "NAMAN IS NAMAN";
LongestPalindrome p = new LongestPalindrome();
char[] c = s.toCharArray();
System.out.print("Length of longest seq is" + p.lps(c,0,c.length-1));
}
}
import java.util.HashSet;
import java.util.Scanner;
/**
* #param args
* We are given a string and we need to find the longest subsequence in that string which is palindrome
* In this code we have used hashset in order to determine the unique set of substring in the given strings
*/
public class NumberOfPalindrome {
/**
* #param args
* Given a string find the longest possible substring which is a palindrome.
*/
public static HashSet<String> h = new HashSet<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
for(int i=0;i<=s.length()/2;i++)
h.add(s.charAt(i)+"");
longestPalindrome(s.substring(0, (s.length()/2)+(s.length()%2)));
System.out.println(h.size()+s.length()/2);
System.out.print(h);
}
public static void longestPalindrome(String s){
//System.out.println(s);
if(s.length()==0 || s.length()==1)
return;
if(checkPalindrome(s)){
h.add(s);
}
longestPalindrome(s.substring(0, s.length()-1));
longestPalindrome(s.substring(1, s.length()));
}
public static boolean checkPalindrome(String s){
//System.out.println(s);
int i=0;int j=s.length()-1;
while(i<=j){
if(s.charAt(i)!=s.charAt(j))
return false;
i++;j--;
}
return true;
}
}
private static int findLongestPalindromicSubsequence(String string) {
int stringLength = string.length();
int[][] l = new int[stringLength][stringLength];
for(int length = 1; length<= stringLength; length++){
for(int left = 0;left<= stringLength - length;left++){
int right = left+ length -1;
if(length == 1){
l[left][right] = 1;
}
else{
if(string.charAt(left) == string.charAt(right)){
//L(0, n-1) = L(1, n-2) + 2
if(length == 2){
// aa
l[left][right] = 2;
}
else{
l[left][right] = l[left+1][right-1]+2;
}
}
else{
//L(0, n-1) = MAX ( L(1, n-1) , L(0, n-2) )
l[left][right] = (l[left+1][right] > l[left][right-1])?l[left+1][right] : l[left][right-1];
}
}
}
}
return l[0][stringLength-1];
}
Program to find the longest palindrome substring from a given string.
package source;
import java.util.ArrayList;
public class LongestPalindrome
{
//Check the given string is palindrome by
public static boolean isPalindrome (String s)
{
StringBuffer sb = new StringBuffer(s);
if(s.equalsIgnoreCase(sb.reverse().toString()))
return true;
else
return false;
}
public static void main(String[] args)
{
//String / word without space
String str = "MOMABCMOMOM"; // "mom" //"abccbabcd"
if(str.length() > 2 )
{
StringBuffer sb = new StringBuffer();
ArrayList<String> allPalindromeList = new ArrayList<>();
for(int i=0; i<str.length(); i++)
{
for(int j=i; j<str.length(); j++)
{
sb.append(str.charAt(j));
if( isPalindrome(sb.toString()) ) {
allPalindromeList.add(sb.toString());
}
}
//clear the stringBuffer
sb.delete(0, sb.length());
}
int maxSubStrLength = -1;
int indexMaxSubStr = -1;
int index = -1;
for (String subStr : allPalindromeList) {
++index;
if(maxSubStrLength < subStr.length()) {
maxSubStrLength = subStr.length();
indexMaxSubStr = index;
}
}
if(maxSubStrLength > 2)
System.out.println("Maximum Length Palindrome SubString is : "+allPalindromeList.get(indexMaxSubStr));
else
System.out.println("Not able to find a Palindrome who is three character in length!!");
}
}
}
for each letter in the string:
set the letter as the middle of the palindrome (current Length = 1)
check how long would be the palindrome if this is its middle
if this palindrome is longer than the one we found (until now) : keep the index and the size of the palindrome.
O(N^2) : since we have one loop that choose the middle and one loop that check how long the palindrome if this is the middle. each loop runs from 0 to O(N) [the first one from 0 to N-1 and the second one is from 0 to (N-1)/2]
for example:
D B A B C B A
i=0 : D is the middle of the palindrome, can't be longer than 1 (since it's the first one)
i=1: B is the middle of the palindrome, check char before and after B : not identical (D in one side and A in the other) --> length is 1.
i=2 : A is middle of the palindrome, check char before and after A : both B --> length is 3. check chars with gap of 2: not identiacl (D in one side and C in the other) --> length is 3.
etc.
Input : A1,A2,....,An
Goal : Find the longest strictly increasing subsequence (not necessarily contiguous).
L(j): Longest strictly increasing subsequence ending at j
L(j): max{ L(i)}+1 } where i < j and A[i] < A[j]
Then find max{ L(j) } for all j
You will get the source code here