This is a very common problem in which we would have to find the longest substring which is also a palindrome substring for the given input string.
Now there are multiple possible approaches to this and I am aware about Dynamic programming solution, expand from middle etc. All these solutions should be used for any practical usecase.
I was experimenting with using recursion to solve this problem and trying to implement the simple idea.
Let us assume that s is the given input string and i and j represent any valid character indexes of input string. So if s[i] == s[j], my longest substring would be:
s.charAt(i) + longestSubstring(s, i + 1, j - 1) + s.charAt(j)
And if these two characters are not equal then:
max of longestSubstring(s, i + 1, j) or longestSubstring(s, i, j - 1)
I tried to implement this solution below:
// end is inclusive
private static String longestPalindromeHelper(String s, int start, int end) {
if (start > end) {
return "";
} else if (start == end) {
return s.substring(start, end + 1);
}
// if the character at start is equal to end
if (s.charAt(start) == s.charAt(end)) {
// I can concatenate the start and end characters to my result string
// plus I can concatenate the longest palindrome in start + 1 to end - 1
// now logically this makes sense to me, but this would fail in the case
// for ex: a a c a b d k a c a a (space added for visualization)
// when start = 3 (a character)
// end = 7 (again end character)
// it will go in recursion with start = 4 and end = 6 from now onwards
// there is no palindrome substrings apart from the single character
// substring (which are palindrome by itself) so recursion tree for
// start = 3 and end = 7 would return any single character from b d k
// let's say it returns b so result would be a a c a b a c a a
// this would be correct answer for longest palindrome subsequence but
// not substring because for sub strings I need to have consecutive
// characters
return s.charAt(start)
+ longestPalindromeHelper(s, start + 1, end - 1) + s.charAt(end);
} else {
// characters are not equal, increment start
String s1 = longestPalindromeHelper(s, start + 1, end);
String s2 = longestPalindromeHelper(s, start, end - 1);
return s1.length() > s2.length() ? s1 : s2;
}
}
public static String longestPalindrome(String s) {
return longestPalindromeHelper(s, 0, s.length() - 1);
}
public static void main(String[] args) throws Exception {
String ans = longestPalindrome("aacabdkacaa");
System.out.println("Answer => " + ans);
}
For a moment let us forgot about time complexity or runtime. I am focused towards making it work for simple case above.
As you can see in the comments I got the idea why this is failing but I tried hard to rectify the problem following the exactly same approach. I don't want to use loops here.
What could be the possible fix for this following same approach?
Note: I am interested in the actual string as answer and not the length. FYI I had a look at all the other questions and it seems no one is following this approach for correctness so I am trying.
Once you have a call wherein s[i] == s[j], you could flip a boolean flag or switch to a modified method that communicates to child calls that they can no longer use the "don't match, try i + 1 and j - 1" branch (else condition). This ensures you're looking at substrings, not subsequences, for the remainder of the recursion.
Secondly, for the substring variant, even if s[i] == s[j], you should also try i + 1 and j - 1 as if these characters didn't match, because one or both of these characters might not be part of the final best substring between i and j. In the subsequence version, there's never any reason not to add any matching characters to the current palindromic subsequence for the range i to j, but that's not always the case with substrings.
For example, given input "aabcbda" and we're at a call frame where i = 1 and j = length - 1, we need to maximize over three possibilities:
The best substring includes both 'a' characters. Call the subroutine with the flag that says we have to consume from both ends on down and can no longer try skipping characters.
The best substring might still include s[i] but not s[j], try j - 1.
The best substring might still include s[j] but not s[i], try i + 1.
Another observation: it might make more sense to pass best indices up the helper call chain, then grab the longest palindromic substring based on these indices at the very end in the wrapper function.
On a similar note, if you're struggling, you might simplify the problem and return the longest palindromic substring length using your recursive method, then switch to getting the actual substring itself. This makes it easier to focus on the subsequence logic without the return value complicating things as much.
It is much easier to use loops here, rather than recursion, something like this:
public static void main(String[] args) {
System.out.println(longestPalindrome("abbqa")); // bb
System.out.println(longestPalindrome("aacabdkacaa")); // aca
System.out.println(longestPalindrome("aacabdkaccaa")); // acca
}
public static String longestPalindrome(String str) {
String palindrome = "";
for (int i = 0; i < str.length(); i++) {
for (int j = i; j < str.length(); j++) {
String substring = str.substring(i, j);
if (isPalindrome(substring)
&& substring.length() > palindrome.length()) {
palindrome = substring;
}
}
}
return palindrome;
}
public static boolean isPalindrome(String str) {
for (int i = 0; i < str.length() / 2; i++) {
if (str.charAt(i) != str.charAt(str.length() - i - 1)) {
return false;
}
}
return true;
}
Related
I have a sequence, and I am trying to make a program to find the nth term of the sequence.
The sequence is as follows:
1, 11, 21, 1211, 111221, 312211...
In this sequence, each term describes the previous term. For example, "1211" means that the previous term; the previous term is "21" where there is one occurrence of a 2 and then one occurrence of a 1 (=1211). To get the third term, "21," you look at the second term: 11. There are two occurrences of a 1 which gives us "21."
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
System.out.println( Main.num(n-1, "1"));
}
public static String num(int times, String x){
if(times == 0){
return x;
}else{
//System.out.println("meow");
String y = "" + x.charAt(0);
int counter = 0;
for(int i = 1; i < x.length(); i++){
if(x.charAt(i) == x.charAt(i-1)){
counter++;
}else{
y += "" + counter + x.charAt(i-1);
counter = 0;
}
}
return num(times--, y);
}
//return "";
}
}
My code uses recursion to find the nth term. But, it gives us errors :(
First, I start of the method "num" by passing it the number of terms-1 (since the first term is already given) and the first term (1).
In the method num, we start off by using a conditional to establish the base case (when you are done finding the nth term).
If the base case is false, then you find the next term in the sequence.
This is a very cool sequence! I like that it is English based and not mathematical, haha. (Though now I wonder ... is there a formula we could make for the nth term? I'm pretty sure it's impossible or uses some crazy-level math, but just something to think about ...)
In your solution, the recursive logic of your code is correct: after you find each term, you repeat the method with your knew number and find the next term using that element, and end when you have determined the first n elements. Your base case is also correct.
However, the algorithm you developed for determining the terms in the sequence is the issue.
To determine the next element in the sequence, we want to:
Logical Error:
Create a empty variable, y, for your next element. The variable, counter, should not start at 0, however. This is because every element will ALWAYS have an occurrence of at least 1, so we should initialize int counter = 1;
Iterate through the characters in x. (You did this step correctly) We begin at i = 1, because we compare each character to the previous one.
If the current character is equal to the previous character, we increment counter by 1.
Otherwise, we concatenate counter and the character being repeated, to y. Remember, to reinitialize counter to 1, not 0.
Technical Errors:
Once we reach the end of iterating x, we need to concatenate our final counter and character to y, since the else statement for the final characters will never run in our for loop.
This is done with the following code: y += "" + counter + x.charAt(x.length() - 1);
Finally, when you are doing your recursive call, you should do --times instead of times--. The difference between these two parameters is that with your original code, you are post-decrementing. This means the value of times is decreasing after the method call, when we want the decreased value to be sent into the method. To solve this, we need to pre-decrement, by doing --times.
import java.util.*;
class CoolSequence {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
System.out.println(num(n, "1"));
}
public static String num(int times, String x){
if(times == 0){
return x;
}
else{
String y = "";
int counter = 1;
for(int i = 1; i < x.length(); i++){
if(x.charAt(i) == x.charAt(i - 1)){
counter++;
}
else{
y += "" + counter + x.charAt(i - 1);
counter = 1;
}
}
y += "" + counter + x.charAt(x.length() - 1);
return num(--times, y);
}
}
}
Testing:
6
13112221
An alternative approach would be using an iterative method:
import java.util.*;
class CoolSequence2 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
ArrayList<String> nums = new ArrayList<String>();
int n = scan.nextInt();
String val = "1";
for(int i = 0; i < n; i++){
String copy = val;
val = "";
while(!copy.equals("")){
char curr = copy.charAt(0);
int ind = 0;
int cons = 0;
while(ind < copy.length() && curr == copy.charAt(ind)){
cons += 1;
ind += 1;
}
val += String.valueOf(cons) + copy.charAt(cons - 1);
copy = copy.substring(cons);
}
nums.add(val);
}
System.out.println(nums.get(nums.size() - 1));
}
}
6
13112221
In this method, we use a for loop to iterate through n terms. To determine each element, we do a similar method to your logic:
We create an empty string, val, to hold the new element, and store our current element in copy. We also initialize a cons, similar to your counter.
While copy is not empty, we iterate through copy and increment cons until there is an element that is not equal to the next element.
When this occurs, we concatenate cons and the repeated element to val, like in your code. Then, we cut out the repeated elements from copy and continue the process.
We add the new value of val to nums, and keep iterating through the n elements.
I hope these two methods of approaching your problem helped! Please let me know if you have any further questions or clarifications :)
You can use Pattern with backreference.
The regular expression "(.)\\1*" matches any single character ("(.)") and zero or more sequences of the same character ("\\1*"). "\\1" is called a backreference, it refers to the string enclosed in parentheses 1st.
For example, it matches 111, 22 and 1 for 111221.
replaceAll() calls the lambda expression specified by the argument each time it matches. The lambda expression receives a MatchResult and returns a string. The matched string is replaced with the result.
The lambda expression in this case concatenates the length of the matched string (match.group().length()) and the first character (match.group(1)).
static final Pattern SEQUENCE_OF_SAME_CHARACTER = Pattern.compile("(.)\\1*");
static String num(int times, String x) {
for (int i = 0; i < times; ++i)
x = SEQUENCE_OF_SAME_CHARACTER.matcher(x).replaceAll(
match -> match.group().length() + match.group(1));
return x;
}
public static void main(String[] args) {
for (int i = 1; i <= 8; ++i)
System.out.print(num(i - 1, "1") + " ");
}
output:
1 11 21 1211 111221 312211 13112221 1113213211
So I'm working on some Java exercises, and one that has caught my attention recently is trying to produce all permutations of a String using iteration. There are plenty of examples online - however, a lot of them seem very complex and I'm not able to follow.
I have tried using my own method, which when tested with a string of length 3 it works fine. The method is to (for each letter) keep moving a letter along the string, swapping it with whatever letter is in front of it. E.g.
index: 012
string: abc
(iteration 1) swap 'a' (index 0) with letter after it 'b' (index 0+1) : bac
(iteration 2) swap 'a' (index 1) with letter after it 'c' (index 1+1) : bca
(iteration 3) swap 'a' (index 2) with letter after it 'b' (index 0) : acb
current permutations: abc (original), bac, bca, acb
(iteration 3) swap 'b' (index 1) with letter after it 'c' (index 1+1) : acb
(iteration 4) swap 'b' (index 2) with letter after it 'a' (index 0) : bca
(iteration 5) swap 'b' (index 0) with letter after it 'c' (index 1) : cba
current permutations: abc (original), bac, bca, acb, acb, cba
...
This is how I implemented it in Java:
String str = "abc"; // string to permute
char[] letters = str.toCharArray(); // split string into char array
int setLength = factorial(letters.length); // amount of permutations = n!
HashSet<String> permutations = new HashSet<String>(); // store permutations in Set to avoid duplicates
permutations.add(str); // add original string to set
// algorithm as described above
for (int i = 0; i < setLength; i++) {
for (int j = 0; j < letters.length; j++) {
int k;
if (j == letters.length - 1) {
k = 0;
} else {
k = j + 1;
}
letters = swap(letters, j, k);
String perm = new String(letters);
permutations.add(perm);
}
}
The problem is if I input a string of length 4, I only end up with 12 permutations (4x3) - if I input a string of length 5, I only end up with 20 permutations (5x4).
Is there a simple modification I could make to this algorithm to get every possible permutation? Or does this particular method only work for strings of length 3?
Appreciate any feedback!
Suppose the input is "abcd". This is how your algorithm will work
bacd
bacd
bcad
bcda
If you observe carefully, "a" was getting positioned at all indexes and the following consecutive letter was getting replaced with "a". However, after your algorithm has produced "bacd" - it should be followed by "badc" also, which will be missing from your output.
For string of length 4, When you calculated the number of permutations as factorial, you understand that the first position can be occupied by 4 characters, followed by 3, 2 and 1. However, in your case when the first two positions are occupied by "ba" there are two possibilities for 3rd position, i.e. c and d. While your algorithm correctly finds "cd", it fails to find "dc" - because, the loop does not break the problem into further subproblems, i.e. "cd" has two permutations, respectively "cd" and "dc".
Thus, the difference in count of your permutations and actual answer will increase as the length of string increases.
To easily break problems into sub-problem and solve it, many algorithm uses recursion.
However, you could look into Generate list of all possible permutations of a string for good iterative answers.
Also, as the length of string grows, calculating number of permutation is not advisable.
While I do not know of a way to expand upon your current method of switching places (I've attempted this before to no luck), I do know of a fairly straightforward method of going about it
//simple method to set up permutate
private static void permutations(String s)
{
permutate(s, "");
}
//takes the string of chars to swap around (s) and the base of the string to add to
private static void permutate(String s, String base)
{
//nothing left to swap, just print out
if(s.length() <= 1)
System.out.println(base + s);
else
//loop through the string of chars to flip around
for(int i = 0; i < s.length(); i++)
//call with a smaller string of chars to flip (not including selected char), add selected char to base
permutate(s.substring(0, i) + s.substring(i + 1), base + s.charAt(i));
}
The goal with this recursion is to delegate as much processing as possible to something else, breaking the problem down bit by bit. It's easy to break down this problem by choosing a char to be first, then telling a function to figure out the rest. This can then be done for each char until they've all been chosen once
I have a certain string and I want to sort it in recursion.
My code is error free but the algorithm is not working and I need help
The index will be zero when calling the function.
The main idea is the compare between indexes in the string and creating a new string each time with the new sequence of the letters compared.
each call I send the new string which was created in each run
private static String sort(String s1, int index)
{
String s2="";
if (index == s1.length()-2)
return s1;
else
{
if (s1.charAt(index) > s1.charAt(index+1))
{
for (int i = 0; i < s1.length(); i++)
{
if (index == i)
{
s2 += s1.charAt(index+1);
s2 += s1.charAt(index);
i += 2;
}
s2 += s1.charAt(i);
}
}
else
{
for (int i = 0; i < s1.length(); i++)
{
if (index == i)
{
s2 += s1.charAt(index);
s2 += s1.charAt(index+1);
i += 2;
}
s2 += s1.charAt(i);
}
}
return (sort(s2,++index));
}
}
input : acbacds
output: abaccds
the output should be : aabccds
Each call compares a pair of adjacent characters; if they're out of order, you switch them.
Your recursion simply replaces an outer loop running through the length of the array.
The end of this process guarantees that the largest value will now be at the end of the array. To this extent, it works correctly.
If you expect an array of N elements to get fully sorted, you must repeat this process up to N-1 times. The only reason your given example is so close is that the array you gave it is already very close to sorted.
Try again with something in reverse order, and you'll see the effect. For instance, use "hgfedcba". One pass will get you "gfedcbah", moving the 'h' from the front to the end.
If you want a working bubble sort, try searching here on SO or on the web overall.
Finally, you might look into the Java substring functions; building s2 a character at a time is hard on the eyes; it's also slow, especially in the case where you don't switch characters.
Given 2 strings, a and b, return the number of the positions where they contain the same length 2 substring. For instance a and b is respectively "xxcaazz" and "xxbaaz" yields 3, since the "xx", "aa", and "az" substrings appear in the same place in both strings.
What is wrong with my solution?
int count=0;
for(int i=0;i<a.length();i++)
{
for(int u=i; u<b.length(); u++)
{
String aSub=a.substring(i,i+1);
String bSub=b.substring(u,u+1);
if(aSub.equals(bSub))
count++;
}
}
return count;
}
In order to fix your solution, you really don't need the inner loop. Since the index should be same for the substrings in both string, only one loop is needed.
Also, you should iterate till 2nd last character of the smaller string, to avoid IndexOutOfBounds. And for substring, give i+2 as second argument instead.
Overall, you would have to change your code to something like this:
int count=0;
for(int i=0; i < small(a, b).length()-1; i++)
{
String aSub=a.substring(i,i+2);
String bSub=b.substring(i,i+2);
if(aSub.equals(bSub))
count++;
}
}
return count;
Why I asked about the length of string is, it might become expensive to create substrings of length 2 in loop. For length n of smaller string, you would be creating 2 * n substrings.
I would rather not create substring, and just match character by character, while keeping track of whether previous character matched or not. This will work perfectly fine in your case, as length of substring to match is 2. Code would be like:
String a = "iaxxai";
String b = "aaxxaaxx";
boolean lastCharacterMatch = false;
int count = 0;
for (int i = 0; i < Math.min(a.length(), b.length()); i++) {
if (a.charAt(i) == b.charAt(i)) {
if (lastCharacterMatch) {
count++;
} else {
lastCharacterMatch = true;
}
} else {
lastCharacterMatch = false;
}
}
System.out.println(count);
The heart of the problem lies with your usage of the substring method. The important thing to note is that the beginning index is inclusive, and the end index is exclusive.
As an example, dissecting your usage, String aSub=a.substring(i,i+1); in the first iteration of the loop i = 0 so this line is then String aSub=a.substring(0,1); From the javadocs, and my explanation above, this would result in a substring from the first character to the first character or String aSub="x"; Changing this to i+2 and u+2 will get you the desired behavior but beware of index out of bounds errors with the way your loops are currently written.
String a = "xxcaazz";
String b = "xxbaaz";
int count = 0;
for (int i = 0; i < (a.length() > b.length() ? b : a).length() - 1; i++) {
String aSub = a.substring(i, i + 2);
String bSub = b.substring(i, i + 2);
if (aSub.equals(bSub)) {
count++;
}
}
System.out.println(count);
I want to write a program that prints words incrementally until a complete sentence appears. For example : I need to write (input), and output:
I
I need
I need to
I need to write.
Here is my code:
public static void main(String[] args) {
String sentence = "I need to write.";
int len = sentence.length();
int numSpace=0;
System.out.println(sentence);
System.out.println(len);
for(int k=0; k<len; k++){
if(sentence.charAt(k)!='\t')
continue;
numSpace++;
}
System.out.println("Found "+numSpace +"\t in the string.");
int n=1;
for (int m = 1; m <=3; m++) {
n=sentence.indexOf('\t',n-1);
System.out.println("ligne"+m+sentence.substring(0, n));
}
}
and this is what I get:
I need to write.
16
Found 0 in the string.
Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
String index out of range: -1 at
java.lang.String.substring(String.java:1937) at
split1.Split1.main(Split1.java:36) Java Result: 1 BUILD SUCCESSFUL
(total time: 0 seconds)
I don't understand why numSpace doesn't count the occurrences of spaces, nor why I don't get the correct output (even if I replace numSpace by 3 for example).
You don't have a \t character, so indexOf(..) returns -1
You try a substring from 0 to -1 - fails
The solution is to check:
if (n > -1) {
System.out.prinltn(...);
}
Your loop looking for numSpace is incorrect. You are looking for a \t which is a tab character, of which there are none in the string.
Further, when you loop in the bottom, you get an exception because you are trying to parse by that same\t, which will again return no results. The value of n in n=sentence.indexOf('\t',n-1); is going to return -1 which means "there is not last index of what you are looking for". Then you try to get an actual substring with the value of -1 which is an invalid substring, so you get an exception.
You are mistaken by the concept of \t which is an escape sequence for a horizontal tab and not for a whitespace character (space). Searching for ' ' would do the trick and find the whitespaces in your sentence.
This looks like homework, so my answer is a hint.
Hint: read the javadoc for String.indexOf paying attention to what it says about the value returned when the string / character is not found.
(In fact - even if this is not formal homework, you are clearly a Java beginner. And beginners need to learn that the javadocs are the first place to look when using an unfamiliar method.)
The easiest way to solve this I guess would be to split the String first by using the function String.split. Something like this:
static void sentence(String snt) {
String[] split = snt.split(" ");
for (int i = 0; i < split.length; i++) {
for (int j = 0; j <= i; j++) {
if (i == 1 && j == 0) System.out.print(split[j]);
else System.out.printf(" %s", split[j]);
}
}
}
As other people pointed out. You are counting every characters except tabs(\t) as a space. You need to check for spaces by
if (sentence.charAt(k) == ' ')
\t represents a tab. To look for a space, just use ' '.
.indexOf() returns -1 if it can't find a character in the string. So we keep looping until .indexOf() returns -1.
Use of continue wasn't really needed here. We increment numSpaces when we encounter a space.
System.out.format is useful when we want to mix literal strings and variables. No ugly +s needed.
String sentence = "I need to write.";
int len = sentence.length();
int numSpace = 0;
for (int k = 0; k < len; k++) {
if (sentence.charAt(k) == ' ') {
numSpace++;
}
}
System.out.format("Found %s in the string.\n", numSpace);
int index = sentence.indexOf(' ');
while(index > -1) {
System.out.println(sentence.substring(0, index));
index = sentence.indexOf(' ', index + 1);
}
System.out.println(sentence);
}
Try this, it should pretty much do what you want. I figure you have already finished this so I just made the code real fast. Read the comments for the reasons behind the code.
public static void main(String[] args) {
String sentence = "I need to write.";
int len = sentence.length();
String[] broken = sentence.split(" "); //Doing this instead of the counting of characters is just easier...
/*
* The split method makes it where it populates the array based on either side of a " "
* (blank space) so at the array index of 0 would be 'I' at 1 would be "need", etc.
*/
boolean done = false;
int n = 0;
while (!done) { // While done is false do the below
for (int i = 0; i <= n; i++) { //This prints out the below however many times the count of 'n' is.
/*
* The reason behind this is so that it will print just 'I' the first time when
* 'n' is 0 (because it only prints once starting at 0, which is 'I') but when 'n' is
* 1 it goes through twice making it print 2 times ('I' then 'need") and so on and so
* forth.
*/
System.out.print(broken[i] + " ");
}
System.out.println(); // Since the above method is a print this puts an '\n' (enter) moving the next prints on the next line
n++; //Makes 'n' go up so that it is larger for the next go around
if (n == broken.length) { //the '.length' portion says how many indexes there are in the array broken
/* If you don't have this then the 'while' will go on forever. basically when 'n' hits
* the same number as the amount of words in the array it stops printing.
*/
done = true;
}
}
}