Fibonacci Words from ACM Contest - java

I'm trying to figure out how to solve one of the problems from one of the ACM ICPC finals (from 2012, so I guess the most recent). It's called Fibonacci Words and is described here under Problem D.
I think I'm very close, as all the test cases except the last are giving the correct answer. But for the last, I'm getting 6440026026380244497, which is on the right order of magnitude but still way off--since the order of magnitude is huge. :)
Here's my Java code, with a lot of comments (too many, you think? could it be refactored?):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class FibonacciWords {
public static StringBuilder[] updateChars(StringBuilder lastOneBack, StringBuilder firstTwoBack, StringBuilder lastTwoBack, StringBuilder firstThreeBack) {
int pattLen = lastOneBack.length();
StringBuilder[] newChars = new StringBuilder[2];
newChars[0] = new StringBuilder(pattLen); // will become the new lastOneBack!
newChars[1] = new StringBuilder(pattLen); // will become the new firstTwoBack!
if(lastOneBack.charAt(0) == 'E') { // lastOneBack not full yet
int shiftCharsBy = 0; // holds amount to shift lastOneBack by
for(int i = 0; i < pattLen; i++) {
if(firstTwoBack.charAt(i) != 'E') {
shiftCharsBy++; // need to move whatever is at beginning of firstTwoBack to end of lastOneBack
} else {
break; // when first 'E' is reached in firstTwoBack, the rest are 'E' also
}
}
for(int i = 0; i < pattLen-shiftCharsBy; i++) {
newChars[0].append(lastOneBack.charAt(i+shiftCharsBy)); // shift lastOneBack by shiftCharsBy characters
}
for(int i = 0; i < shiftCharsBy; i++) {
newChars[0].append(firstTwoBack.charAt(i)); // fill remainder of new lastOneBack with what's in firstTwoBack
}
} else { // lastOneBack already full
newChars[0] = lastTwoBack; // make lastOneBack lastTwoBack (following pattern)
}
if(firstTwoBack.charAt(pattLen-1) == 'E') { // firstTwoBack not full yet
if(lastOneBack.charAt(0) == 'E') {
for(int i = 1; i < pattLen; i++) {
if(lastOneBack.charAt(i) != 'E') {
newChars[1].append(lastOneBack.substring(i)); // move last characters of lastOneBack to front of new firstTwoBack
break;
}
}
int charsAdded = newChars[1].length();
for(int i = 0; i < pattLen-charsAdded; i++) {
newChars[1].append('E'); // fill whatever might remain with 'E'
}
} else {
//newChars[1] = lastOneBack;
for(int i = 0; i < pattLen; i++) {
if(firstTwoBack.charAt(i) != 'E') {
newChars[1].append(firstTwoBack.charAt(i));
} else {
break;
}
}
int charsAdded = newChars[1].length();
// now take from firstThreeBack--which also isn't full if firstTwoBack isn't--until pattLen is reached
for(int i = 0; i < pattLen-charsAdded; i++) {
newChars[1].append(firstThreeBack.charAt(i));
}
}
} else {
newChars[1] = firstTwoBack; // firstTwoBack doesn't change when already full
}
return newChars;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
String pattern = br.readLine();
long numPattTwoPrior = 0; // number of times pattern occurred in F(n-2)
if(pattern.equals("0")) {
numPattTwoPrior++; // since n starts at 2 below, increment this if pattern is F(0), or "0"
}
long numPattOnePrior = 0; // number of times pattern occurred in F(n-1)
if(pattern.equals("1")) {
numPattOnePrior++; // since n starts at 2 below, increment this if pattern is F(1), or "1"
}
int pattLen = pattern.length();
StringBuilder lastCharsInOnePrior = new StringBuilder(pattLen); // keeps track of last pattLen characters in F(n-1)
for(int i = 0; i < pattLen; i++) {
lastCharsInOnePrior.append('E'); // 'E' stands for empty
}
lastCharsInOnePrior.setCharAt(pattLen-1, '1'); // since F(1) = "1"
StringBuilder firstCharsInTwoPrior = new StringBuilder(pattLen);
for(int i = 0; i < pattLen; i++) {
firstCharsInTwoPrior.append('E');
}
firstCharsInTwoPrior.setCharAt(0, '0'); // since F(0) = "0"
StringBuilder lastCharsInTwoPrior = null; // last characters always the same as 2 back, so keep track of this
StringBuilder firstCharsInThreePrior = null; // used for special case in updateChars
// number of times pattern occurs in F(n)
long numPattCurr = (n == 0) ? numPattTwoPrior : (n == 1) ? numPattOnePrior : 0;
for(int i = 2; i <= n; i++) { // finding F(n) up to the n given by the input
numPattCurr = numPattTwoPrior + numPattOnePrior; // at least this many times in F(n)
// adding to above all patterns found as part of concatenating F(n-1) and F(n-2), but not either on its own
middle:
for(int j = 1; j < pattLen; j++) { // starting at pos. 1 b/c [0, pattLen) is all F(n-1)
if(lastCharsInOnePrior.charAt(j) == 'E') {
continue;
}
StringBuilder compareWith = new StringBuilder(pattLen); // to compare with pattern
for(int k = 0; k < pattLen; k++) {
if(j + k >= pattLen) { // reached end of characters in F(n-1), start checking F(n-2)
int posInFirstChars = (j + k) % pattLen;
if(firstCharsInTwoPrior.charAt(posInFirstChars) == 'E') {
break middle; // none of the remaining overlap between F(n-1) and F(n-2) is as long as pattern, so can stop here
} else {
compareWith.append(firstCharsInTwoPrior.charAt(posInFirstChars));
}
} else {
compareWith.append(lastCharsInOnePrior.charAt(j + k));
}
}
if(pattern.equals(compareWith.toString())) {
numPattCurr++; // this overlap matched pattern
}
}
// changing characters of F(n-1) and F(n-2), as needed, for next iteration
StringBuilder[] updatedChars = updateChars(lastCharsInOnePrior, firstCharsInTwoPrior, lastCharsInTwoPrior, firstCharsInThreePrior);
lastCharsInTwoPrior = lastCharsInOnePrior;
firstCharsInThreePrior = firstCharsInTwoPrior;
lastCharsInOnePrior = updatedChars[0];
firstCharsInTwoPrior = updatedChars[1];
// changing number of times pattern found for F(n-1) and F(n-2) for next iteration
numPattTwoPrior = numPattOnePrior;
numPattOnePrior = numPattCurr;
}
System.out.println(numPattCurr);
System.exit(0);
}
}
I think I only left in one line of commented code, which gave the exact same answers in all the test cases when that was the only line within its else block--though what I replaced it with is more correct.
Any suggestions on what I'm missing or how I might go about debugging the last test case? Or interested in talking it through with someone because you're preparing for the contest or just working on learning algorithms? Please let me know.

C++ solution with verification (online judge)

Related

How do you solve this problem with a nested for loop

My goal is to print out names with 2 letters that are the same with only one of the same letter in the name. For example brook would be printed out as brok and michelle would be printed out as michel. Whats wrong with my code?
for(int a = 0; a < word_1.length(); a++)
{
mychar = word_1.charAt(a);
for(int c = 1; c <word_1.length(); c++)
{
mychar_2 = word_1.charAt(c);
if(mychar == mychar_2)
{
word_3 = word_3 + "";
}
else
{
word_3 = word_3 + mychar;
}
}
}
System.out.println(word_3);
Better question is "what is right with the code". Basically the idea looks like going through all letters and then going through all matches. This is not too bad and we can slightly change your code to make it work (see comments):
for(int a = 0; a < word_1.length(); a++)
{
char mychar = word_1.charAt(a);
// flag for matching letters
Boolean found = false;
// you go through only previous characters!
for(int c = 0; !found && c < a; c++)
{
char mychar_2 = word_1.charAt(c);
if(mychar == mychar_2)
{
found = true;
}
}
// here you add character if no match found
if(!found) {
word_3 = word_3 + mychar;
}
}
This is a good way in academic coding, but in actual project if you deal with large numbers of words, you have to consider Map based solution to keep all existing letters and iterate only once through each word. Or you can search for some methods in numerous java libraries to do the job
This would solve the problem:
String testWord = "brook";
List<String> testSet = new ArrayList<String>();
for (int i = 0; i < testWord.length(); i++) {
if (!testSet.contains(String.valueOf(testWord.charAt(i)))) {
testSet.add(String.valueOf(testWord.charAt(i)));
}
}
StringBuilder builder = new StringBuilder();
for (Object s : testSet.toArray()) {
builder.append((String) s);
}
String str = builder.toString();
System.out.println(str);

How to use/modify Knuth-Morris-Pratt algorithm to convert any given string to palindrome

I have been given a task to create a class that given a String will create a palindrome with minimum number of assertions.
Example Runs:
Input: 123333
Output: 12333321
Input: 789
Output: 78987
Input: 1221
Output: 1221221
**Note a Palindrome should NOT return the same Palindrome.
I tried using a modified KMP algorithm as stated here.
I revert the string and compare it to the reverse + original string and then add the mismatches to the original string.
However my function only works for inputs with trailing digits (first example input) however an input like 1234 will return 1234123, '92837465' will return '928374659283746'
public static int[] computelps(String sample){
int[] lps = new int[sample.length()];
lps[0] = 0;
int i = 1;
int len = 0; // length of previous longest prefix suffix
while (i < sample.length()) {
if (sample.charAt(i) == sample.charAt(len)) {
len++;
lps[i] = len;
i++;
}
else
{
if (len != 0) {
len = lps[len - 1];
}
else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
public static void Solution(File samplefile) throws Exception {
BufferedReader br = new BufferedReader(new FileReader(samplefile));
String firstline = br.readLine();
String line;
while ((line = br.readLine()) != null) {
String reverse_str = "";
String newline = line.replace(".", "");
for (int i = newline.length() - 1; i >= 0; i--) {
reverse_str += newline.charAt(i);
}
int [] lps = computelps(reverse_str); // computes the lps of the pattern string
String tot = reverse_str + newline;
// KMP Algorithm modified.
int x = 0; // index for total_string(tot)
int y = 0; // index for pattern
String palindrome = newline;
while (x < tot.length()){
if(reverse_str.charAt(y) == tot.charAt(x)){
y++;
x++;
}
if(y == reverse_str.length()) {
y = lps[y - 1];
}
else if( x < tot.length() && (reverse_str.charAt(y) != tot.charAt(x))){
palindrome += tot.charAt(x);
if ( y!= 0){
y = lps[y-1];
}
else{
x += 1;
}
}
}
System.out.println(palindrome);
}
}
I would appreciate any help. I find algorithms very challenging so please bear with me if my approach or code is sub-par.
*I fixed sample inputs and outputs as well as added my results.
It helps to split this problem in smaller problems, implement a separate method for each and check to see if each method works as expected. What will really help you will be to learn to use the debugger in your Ide. But until you do that you can test that each part of your code works as expected. So I simplified a little your code and split it up :
public static void main(String[] args){
System.out.println("computelps " + ("[0, 0, 0, 0]".equals(Arrays.toString(computelps("4321"))) ? "works" : "doesn't work" ));
System.out.println("reverse " + ("4321".equals(reverse("1234")) ? "works" : "doesn't work" ));
System.out.println("Solution " + ("1234321".equals(Solution("1234")) ? "works" : "doesn't work" ));
}
public static int[] computelps(String sample){
int[] lps = new int[sample.length()];
lps[0] = 0;
int i = 1;
int len = 0; // length of previous longest prefix suffix
while (i < sample.length()) {
if (sample.charAt(i) == sample.charAt(len)) {
len++;
lps[i] = len;
i++;
}
else
{
if (len != 0) {
len = lps[len - 1];
}
else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
public static String Solution(String line) {
String newline = line.replace(".", "");
String reverse_str = reverse(newline);
int [] lps = computelps(reverse_str); // computes the lps of the pattern string
// KMP Algorithm modified.
return kpmModified(newline, reverse_str, lps);
}
private static String kpmModified(String newline, String reverse_str, int[] lps) {
int x = 0; // index for total_string(tot)
int y = 0; // index for pattern
String tot = reverse_str + newline;
String palindrome = newline;
while (x < tot.length()){
if(reverse_str.charAt(y) == tot.charAt(x)){
y++;
x++;
}
if(y == reverse_str.length()) {
y = lps[y - 1];
}
else if( x < tot.length() && (reverse_str.charAt(y) != tot.charAt(x))){
palindrome += tot.charAt(x);
if ( y!= 0){
y = lps[y-1];
}
else{
x += 1;
}
}
}
return palindrome;
}
private static String reverse(String newline) {
String reverse_str = "";
for (int i = newline.length() - 1; i >= 0; i--) {
reverse_str += newline.charAt(i);
}
return reverse_str;
}
And the result is
computelps works
reverse works
Solution doesn't work
So your bug is in kpmModified method. I can't spend more time and I'm not familiar with the algorithm but you should continue like this and figure our what part of that method has the bug.
I think you overthink the problem. The question is basically adding a string's reversed version back to it's original, but not every character, right? So you might need to find something like a pointer to tell the function where to start to reverse.
One example. Let the string be 12333. If we add every character from the index string.length() to 0, it will be 1233333321, which is not correct, since there are duplicated 3's. We need to ignore those, so we need to add characters from string.length() - numOfDuplicateAtEnd to 0.
public String palindromic(String num) {
int i = num.length() - 1;
while (i > -1 && num.charAt(i) == num.charAt(num.length() - 1))
i--;
for (int k = i; k > -1; --k)
num += num.substring(k, k + 1);
return num;
}

New to programming, getting a run time error don't know why

Basically summarized in the title. https://ideone.com/E2BMS8 <-- that's a link to the code. I understand if you don't want to click it though so I'll paste it here as well. will just be disorganized. The code is supposed to flip the letters but keep words in the same position. I would like to figure that part out on my own though. Just need help with the run time error.
import java.util.*;
class Ideone {
public static void main (String[] args) throws java.lang.Exception {
Scanner input = new Scanner(System.in);
String sent, accum = "";
char check, get;
int len, count = 0;
System.out.print("Please enter the sentance you want reversed: ");
sent = input.nextLine();
len = sent.length();
for (int i = 0; i < len; i++) {
check = sent.charAt(len - i);
count += 1;
if (check == ' ') {
for (int p = 0; p < count; p++) {
while (p < count) {
get = sent.charAt(len - p);
accum += (get + ' ');
}
}
}
}
System.out.println("Reversed: " + accum);
}
}
The error String index out of range is cause because of the len is one more than the index range. Remove one on the index such I did below:
import java.util.*;
public class Ideone {
public static void main (String[] args) throws java.lang.Exception {
Scanner input = new Scanner(System.in);
String sent, accum = "";
char check, get;
int len, count = 0;
System.out.print("Please enter the sentance you want reversed: ");
sent = input.nextLine();
len = sent.length();
for (int i = 0; i < len; i++) {
check = sent.charAt(len - i - 1);
count += 1;
if (check == ' ') {
for (int p = 0; p < count; p++) {
get = sent.charAt(len - p - 1);
accum += (get + ' ');
}
}
}
System.out.println("Reversed: " + accum);
}
}
This is a classical "off by one" error -- something you will run into a lot as you find your programming feet. The issue in this case is the 0-based indexing. That is, the first character of a string is at index 0, and the last is at index "string length - 1". If we use sent = "Test"; as an example, then:
sent.charAt(0) == 'T'
sent.charAt(1) == 'e'
sent.charAt(2) == 's'
sent.charAt(3) == 't'
sent.charAt(4) == ??? // "That's an error, Jim!"
Note that index 4 -- which perhaps confusingly is also the length of the string -- is out of bounds. So, what happens during the first iteration of the loop, when i == 0:
check = sent.charAt(len - i); // ERROR! Because ...
==> = sent.charAt((4) - (0));
==> = sent.charAt( 4 ); // Doh!
I leave it to you to figure out how you might fix it.

Java - replacing word.charAt(i) with word.charAt(i+1) and making word.charAt(i+1) = 'A'

I need to look at each word in an array, so I made a for loop with array[i] equaling String word, then I made another for loop that has an if statement that is changing word.charAt(i) with word.charAt(i+1) and changing word.charAt(i+1) = 'A'.
The objective is to 'encrypt' the word by checking if i is equal to 'A', and if it is then switching positions with i+1.
Here is my code:
for(int i = 0; i < unchangedFileInput.length; i++) {
String word = unchangedFileInput[i];
for(int v = 0; v < word.length();v++) {
if (word.charAt(v) == 'A'
&& word.charAt(v+1) != 'A'
&& word.length() > 1) {
char[] mywordChars = word.toCharArray();
temp = mywordChars[v+1];
mywordChars[v] = mywordChars[v+1];
mywordChars[v+1] = 'A';
word = String.valueOf(mywordChars);
} else {
System.out.println(word);
}
}
System.out.println(word);
}
unchangedFileInput is an array that has the values of:
AVACADO
CHICKEN
BACON
AARDVARK
This is what the final result should look like:
AVACADO = VACADAO
CHICKEN = UNCHANGED
BACON = BCAON
AARDVARK = ARADVRAK
NAAN = NANA
You need to check that you don't go past the array limit first. I created a test framework,
public static void main(String[] args) {
String[] unchangedFileInput = { "AVACADO", "CHICKEN", "BACON",
"AARDVARK", "NAAN" };
String[] changedFileInput = new String[unchangedFileInput.length];
for (int i = 0; i < unchangedFileInput.length; i++) {
changedFileInput[i] = transformName(unchangedFileInput[i]);
System.out.printf("%s = %s%n", unchangedFileInput[i],
changedFileInput[i]);
}
}
And then wrapped your code in a method (and just created a new String with a StringBuilder removing the array conversions),
private static String transformName(String in) {
StringBuilder sb = new StringBuilder();
for (int i = 0, len = in.length(); i < len; i++) {
char ch = in.charAt(i);
if (ch == 'A') {
if (i + 1 < in.length() && in.charAt(i + 1) != 'A') {
sb.append(in.charAt(i + 1));
i++;
}
}
sb.append(ch);
}
return sb.toString();
}
And I get your requested output,
AVACADO = VACADAO
CHICKEN = CHICKEN
BACON = BCAON
AARDVARK = ARADVRAK
NAAN = NANA
In order to achieve what you want you need to start the iteration from the end of the word to the beginning, otherwise, when you swap 'A' with the following character, you will have the 'A' again at the next iteration. If you start from the end, you will hit each 'A' only once.
Using this code
for(int v = 0; v < word.length();v++) {
if (word.charAt(v) == 'A' && word.charAt(v+1)
You will get an IndexOutOfBoundsException at the end of the String.
And if you are talking about the IndexOutOfBoundsException error!!
I suggested, v < word.length() - 1;

String Compression loop logic

My for loop for my string compression is a bit off. I have been working on this assignment the past 5 days and I can't figure out for the life of me what is wrong. Can someone help me out?
For example, I passed over the string "TTTTrrrEe" and instead of getting T4r3Ee, I'm getting T4r3EeTT. I don't know why it jumps back to the beginning of the string like that, but I am getting closer.We can only use charAt,equals,length, and substring from the string class.
Can someone help guide me in the right direction by helping to correct my logic? I still want to try and code this myself, seeing as how it is an assignment.
public static String compress(String s){
int count = 0;
String temp = s.substring(0,1);
for(int i = 0; i < s.length(); i++){
if(i !=s.length()-1){
if(temp.equals(s.substring(i,i+1))){
count++;
}else{
if(count < 1){
System.out.print(s.substring(i,i+2));
System.out.print(temp.substring(0,1) );
}else{
System.out.print("" + temp.substring(0,1) + count);
i--;
temp = s.substring(count,count+1);
System.out.println(" temp is now " + temp);
count = 0;
//i--;
}
}
}
}
System.out.println(temp);
return temp;
}
Since this is a learning exercise, I wouldn't try fixing your code, just point out a few things to work on to get it right:
The if (i !=s.length()-1) condition inside the loop becomes unnecessary if you change your for loop condition to i < s.length()-1
Comparing individual characters is easier (and faster) than comparing substrings. You get a character at position i by calling char ch1 = s.charAt(i), and compare two characters using == operator, rather than calling equals() on them.
When count is zero (your count < 1 condition is equivalent to count == 0) you print both the current character and the character after it, in addition to the first character of temp followed by the count. This does not look correct.
Rather than growing temp as you go through the loop, you set it on each iteration. This does not look correct.
A better way of growing temp as you go through the loop is using StringBuilder and append(), instead of using a plain String, and performing concatenations.
Try using some logic like this;
int count = 0;
for(int i =0; i < string.length()-1; i++){
if(string.charAt(i) == string.charAt(i + 1)){
count++;
// DO SOME OPERATION
}
}
temp = s.substring(count,count+1); does not relate to a position (i), but a size.
In fact I would try to rewrite it afresh, with externally sensible names:
char repeatedChar = `\u0000`; // Not present.
int repetitions = 0;
Because of the no-namer count you got into trouble.
Working code:
public class HelloWorld {
public static void compress(String s){
StringBuilder buff = new StringBuilder();
char tmp = '\0';
int index = 1;
for(int i = 0; i < s.length(); i++){
char curr = s.charAt(i);
if(buff.length() == 0){
tmp = curr;
buff.append(tmp);
continue;
}
if(curr == tmp){
index++;
}
else{
if(index > 1){
buff.append(index);
index = 1;
tmp = curr;
}
buff.append(curr);
}
}
System.out.println(buff.toString());
}
public static void main(String args[]){
compress("TTTTrrrEe");
}
}
Output: T4r3Ee
For compress("TTsssssssssssTTrrrEe");
Output: T2s11T2r3Ee
String temp = s.substring(0,1);
temp.equals(s.substring(i,i+1))
In case of these 2 sentences you should have used a char instead of String, as such:
char temp = s.charAt(0)
temp == s.charAt(i)
I would start with 3 variables:
char lastCharacter = inputString.charAt(0);
int count = 1;
String result = "";
then proceed to process the input string in a loop:
if (length <= 1) return inputString;
for i = 1 ; i < length;i++
if (inputString.charAt(i) == lastCharacter && i != length-1)
count++
else
if count == 1
result += lastCharacter
else
result = result + lastCharacter + count;
count = 1;
end if
lastCharacter = inputString.charAt(i);
end if
end for
return result;
TRY THIS
public class Compress {
/**
* #param args
* #author Rakesh KR
*/
public static String encode(String source) {
StringBuffer dest = new StringBuffer();
for (int i = 0; i < source.length(); i++) {
int runLength = 1;
while (i+1 < source.length() && source.charAt(i) == source.charAt(i+1)) {
runLength++;
i++;
}
dest.append(source.charAt(i));
dest.append(runLength);
}
return dest.toString();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String example = "aaaaaaBBBBccc";
System.out.println("Encode::"+encode(example));
}
}

Categories

Resources