Given A morse String eg. aet = ".- . -" if the spaces are removed it will become an ambiguous morse string ".-.-" which can represent "aet","eta","ent","etet" etc.
the problem is to find the no.of words that the morse string without spaces can represent irrespective of the meaning of the words. The constraint is that the new word which is formed should be the same size of the input i.e "aet" = "ent" and other words like "etet" should be discarded.
i implemented a recursive solution for some reason it is not working. below is my code and thinking of converting this to DP approach to increase time efficiency. Can some one help to point out the mistake in the below code and is DP a right approach to follow for this problem? Thanks in advance!!
EDIT 1 :- The program gives me an output but not the correct one. for ex. for the morse String representing aet = ".- . -" if given without any spaces to the program ".-.-" it should give an out put "3" i.e 3 words can be formed that is of the same size as the input including the input "aet","eta","ent" but it gives me an output "1". I think there is some thing wrong with the recursive calls.
The approach used here is to simply cut the morse string in a place where first valid morse code is encountered and the repeat the process with the rest of the string untill 3 such valid morse code are found and check whether whole morse string is consumed. if consumed increment the word count and repeat the process for different values of substring size(end variable in the below code).
I hope this helps!!.Tried my best to explain as clearly as I could.
import java.util.*;
import java.io.*;
import java.math.*;
import java.text.*;
public class MorseCode2 {
static Map<String,String> morseCode;
static Map<String,String> morseCode2;
static int count = 0;
public static void main(String args[]){
String[] alpha = {"a","b","c","d","e","f","g","h","i","j","k",
"l","m","n","o","p","q","r","s","t","u","v",
"w","x","y","z"};
String[] morse = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",
".--","-..-","-.--","--.."};
morseCode = new HashMap<String,String>();
morseCode2 = new HashMap<String,String>();
for(int i = 0;i<26;i++){
morseCode.put(morse[i],alpha[i]);
}
for(int i = 0;i<26;i++){
morseCode2.put(alpha[i],morse[i]);
}
Scanner in = new Scanner(System.in);
String input = in.next();
String morseString = "";
for(int j = 0; j< input.length(); j++){
morseString += morseCode2.get(input.charAt(j)+"");
}
countPossibleWord(morseString,input.length(),0,1,0);
System.out.println(count);
in.close();
}
public static void countPossibleWord(String s,int inputSize,int start,int end,int tempCount){
if(start >= s.length() || end > s.length()){
return;
}
if(tempCount>inputSize){
return;
}
String sub = s.substring(start, end);
if(sub.length()>4){
return;
}
if(morseCode.get(sub)!=null){
tempCount++;
countPossibleWord(s,inputSize,end,end+1,tempCount);
}
else{
countPossibleWord(s,inputSize,start,end+1,tempCount);
}
if(tempCount == inputSize && end == s.length()){
count++;
}
countPossibleWord(s,inputSize,start,end+1,0);
}
}
EDIT 2 :- Thank you all for your Responses and Extremely sorry for the confusing code, will surely try to improve on writing neat and clear code. learnt a lot from your replies!!
And i also some how made the code work, the problem was I passed wrong argument which changed the state of the recursive calls. Instead of passing "tempCount-1" for the last argument in the last function call in the method "countPossibleWord" i passed "0" this altered the state. found this after running through the code manually for larger inputs. below is the corrected method
public static void countPossibleWord(String s,int inputSize,int start,int end,int tempCount){
if(start >= s.length() || end > s.length()){
return;
}
if(tempCount>inputSize){
return;
}
String sub = s.substring(start, end);
if(sub.length()>4){
return;
}
if(morseCode.get(sub)!=null){
tempCount++;
countPossibleWord(s,inputSize,end,end+1,tempCount);
}
else{
countPossibleWord(s,inputSize,start,end+1,tempCount);
}
if(tempCount == inputSize && end == s.length()){
count++;
}
countPossibleWord(s,inputSize,start,end+1,tempCount-1);
}
}
If you like to have a recursive function, you should be clear about your parameters (use as few as possible) as well as when to step down and when to go up again.
My solution would look something like
public static int countPossibleWord(String strMorse, String strAlpha, int inputSize) {
if (strMorse.length() > 0) { // still input to process
if (strAlpha.length() >= inputSize)
return 0; // String already has wrong size
int count = 0;
for (int i = 0; i < morse.length; i++) { // try all morse codes
if (strMorse.startsWith(morse[i])) { // on the beginning of the given string
count += countPossibleWord(strMorse.substring(morse[i].length()), strAlpha+alpha[i], inputSize);
}
}
return count;
} else {
if( strAlpha.length() == inputSize ) {
System.out.println( strAlpha );
return 1; // one solution has been found
} else {
return 0; // String has wrong size
}
}
}
Your morse and alpha arrays need to be static variables for this to work.
Note that there is only one situation where the recursion will step down: when there is some input left and the size limit is not reached. Then it will check for the next possible letter in the loop.
All other cases will lead the recursion to go one step up again - and when going up, it will return the number of solutions found.
Call it like this:
System.out.println(countPossibleWord(morseString, "", input.length() ));
The fact that you use a class variable instead of the returned value of the recursive function makes it extremely unclear. Even for you as #Thomas Weller said. You should clarify the possible cases when a count one more letter. I deleted eclipse, hence I coded it in C, I hope I will still help you to understand the algo :(understand char* as string)
char morse[26][5] = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",
".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."};
int countPossibleWord(char* s, int inputSize, int start, char* buffer, int sizeBuff){
if(start == inputSize){
if(sizeBuff == 0) return 1;
else return 0;
}
char buff[sizeBuff+2]; //
strncpy(buff, buffer, sizeBuff);//
buff[sizeBuff] = s[start]; // buff = buff+s[start]
buff[sizeBuff+1] = '\0'; //
for(int i = 0; i < 26; ++i){
//run the equivalent of your map to find a match
if(strcmp(buff, morse[i]) == 0)
return countPossibleWord(s, inputSize, start+1, "", 0) + countPossibleWord(s, inputSize, start+1, buff, sizeBuff+1);
}
return countPossibleWord(s, inputSize, start+1, buff, sizeBuff+1);
}
The problem with your code is, that you don't understand it any more, because it's not clean as described by Robert C. Martin. Compare your code to the following. This is certainly still not the cleanest, but I think you can understand what it does. Tell me if you don't.
Consider this main program:
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
String morsetext = enterTextOnConsole();
MorseTable morseTable = new MorseTable();
MorseCode code = convertToMorseCodeWithoutSpaces(morsetext, morseTable);
List<String> guesses = getAllPossibleMeanings(code, morseTable);
List<String> guessesOfSameLength = filterForSameLength(morsetext, guesses);
printListOnConsole(guessesOfSameLength);
}
private static void printListOnConsole(List<String> guessesOfSameLength) {
for (String text : guessesOfSameLength) {
System.out.println(text);
}
}
private static List<String> filterForSameLength(String morsetext, List<String> guesses) {
List<String> guessesOfSameLength = new LinkedList<String>();
for (String guess : guesses) {
if (guess.length() == morsetext.length())
{
guessesOfSameLength.add(guess);
}
}
return guessesOfSameLength;
}
private static List<String> getAllPossibleMeanings(MorseCode code, MorseTable morseTable) {
MorseCodeGuesser guesser = new MorseCodeGuesser(morseTable);
List<String> guesses = guesser.guess(code);
return guesses;
}
private static MorseCode convertToMorseCodeWithoutSpaces(String morsetext, MorseTable morseTable) {
MorseCode code = new MorseCode(morseTable);
code.fromText(morsetext);
code.stripSpaces();
return code;
}
private static String enterTextOnConsole() {
Scanner scanner = new Scanner(System.in);
String text = scanner.next();
scanner.close();
return text;
}
}
and the following MorseTable class:
import java.util.HashMap;
import java.util.Map;
public class MorseTable {
private static final Map<String, String> morseTable;
private static int longestCode = -1;
static
{
morseTable = new HashMap<String, String>();
morseTable.put("a", ".-");
morseTable.put("b", "-...");
morseTable.put("c", "-.-.");
morseTable.put("e", ".");
morseTable.put("t", "-");
morseTable.put("n", "-.");
// TODO: add more codes
for (String code : morseTable.values()) {
longestCode = Math.max(longestCode, code.length());
}
}
public String getMorseCodeForCharacter(char c) throws IllegalArgumentException {
String characterString = ""+c;
if (morseTable.containsKey(characterString)) {
return morseTable.get(characterString);
}
else {
throw new IllegalArgumentException("No morse code for '"+characterString+"'.");
}
}
public int lengthOfLongestMorseCode() {
return longestCode;
}
public String getTextForMorseCode(String morseCode) throws IllegalArgumentException {
for (String key : morseTable.keySet()) {
if (morseTable.get(key).equals(morseCode)) {
return key;
}
}
throw new IllegalArgumentException("No character for morse code '"+morseCode+"'.");
}
}
and the MorseCode class
public class MorseCode {
public MorseCode(MorseTable morseTable)
{
_morseTable = morseTable;
}
final MorseTable _morseTable;
String morseCode = "";
public void fromText(String morsetext) {
for(int i=0; i<morsetext.length(); i++) {
char morseCharacter = morsetext.charAt(i);
morseCode += _morseTable.getMorseCodeForCharacter((morseCharacter));
morseCode += " "; // pause between characters
}
}
public void stripSpaces() {
morseCode = morseCode.replaceAll(" ", "");
}
public MorseCode substring(int begin, int end) {
MorseCode subcode = new MorseCode(_morseTable);
try{
subcode.morseCode = morseCode.substring(begin, end);
} catch(StringIndexOutOfBoundsException s) {
subcode.morseCode = "";
}
return subcode;
}
public MorseCode substring(int begin) {
return substring(begin, morseCode.length());
}
public String asPrintableString() {
return morseCode;
}
public boolean isEmpty() {
return morseCode.isEmpty();
}
}
and last not least, the MorseCodeGuesser
import java.util.LinkedList;
import java.util.List;
public class MorseCodeGuesser {
private final MorseTable _morseTable;
public MorseCodeGuesser(MorseTable morseTable) {
_morseTable = morseTable;
}
public List<String> guess(MorseCode code) {
List<String> wordList = new LinkedList<String>();
if (code.isEmpty()) return wordList;
for(int firstCodeLength=1; firstCodeLength<=_morseTable.lengthOfLongestMorseCode(); firstCodeLength++) {
List<String> guesses = guess(code, firstCodeLength);
wordList.addAll(guesses);
}
return wordList;
}
private List<String> guess(MorseCode code, int firstCodeLength) {
MorseCode firstCode = code.substring(0, firstCodeLength);
String firstCharacter;
try{
firstCharacter = _morseTable.getTextForMorseCode(firstCode.asPrintableString());
} catch(IllegalArgumentException i) {
return new LinkedList<String>(); // no results for invalid code
}
MorseCode remainingCode = code.substring(firstCodeLength);
if (remainingCode.isEmpty()) {
List<String> result = new LinkedList<String>();
result.add(firstCharacter); // sole result if nothing is left
return result;
}
List<String> result = new LinkedList<String>();
List<String> remainingPossibilities = guess(remainingCode);
for (String possibility : remainingPossibilities) {
result.add(firstCharacter + possibility); // combined results
}
return result;
}
}
I have pasted my own solution to it. I have followed DFS and it is giving the correct answer for the given problem statement. Please ask if there are any queries.
alpha =["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
key = [".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--",
"-..-","-.--","--.."]
dic = dict(list(zip(key,alpha)))
def morse_code(morse,count,res,char,length):
global dic
if count == length - 1:
if morse[char:] in dic:
res = res + 1
return res
word = ''
for i in range(char,len(morse)):
word = word + morse[i]
if word not in dic:
continue
else:
count = count + 1
res = morse_code(morse,count,res,i+1,length)
count = count - 1
return res
if __name__ = 'main'
inp = input()
morse = ''
for i in inp:
morse = morse + key[ord(i)-ord('a')]
result = morse_code(morse,0,0,0,len(inp))
print(result)
I am working on a problem on UVa for general programming practice, as I want to get better at programming competitively. However I am having trouble with this problem - Roman Numerals. In this problem the goal is to take input which will be in the form of either a Roman numeral or Arabic numeral and then I must convert from one to the other. I feel that my code should not have trouble in processing fast enough yet according to the online judge, it does not process fast enough. I need to help finding out how I may optimize my code so that it will run faster and not receive TLE.
Below is my program, any help as to explaining why I am receiving Time Limit Exceeded would be greatly appreciated. Thanks in advance.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
class Main {
private static String order = "IVXLCDM";
private static String order2 = "IXCM"; // These chars are the result of 10^n (n depending on index in the string)
private static String order3 = "VLD"; // These chars are products of 5*10^n (n depending on index in the string)
public static void main(String[] args) {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String ans = "";
while (true) {
String read = "";
int aNum = 0;
String rNum = "";
try {
read = br.readLine();
if (read=="")
break;
} catch (IOException e) {
if (read=="")
break;
e.printStackTrace();
}
try {
aNum = Integer.parseInt(read);
// System.out.println(aNum);
int thousands = aNum/1000;
// System.out.println(thousands);
int hundreds = aNum/100;
hundreds = hundreds%10;
// System.out.println(hundreds);
int tens = aNum%100;
tens = tens/10;
// System.out.println(tens);
int ones = aNum%10;
// System.out.println(ones);
rNum+= a2R(thousands,"M");
rNum+= a2R(hundreds,"C");
rNum+= a2R(tens,"X");
rNum+= a2R(ones,"I");
// System.out.println(rNum);
ans+=(rNum+"\n");
// System.out.print(ans);
} catch (NumberFormatException c) {
rNum = read;
if (rNum.equals(""))
break;
aNum = r2A(rNum);
// System.out.println(aNum);
ans+=(aNum+"\n");
// System.out.print(ans);
}
}
System.out.print(ans);
}
private static int r2A(String rNum) {
int aNum = 0;
for (int i = order.length()-1; i >= 0; i--) {
char curChar = order.charAt(i);
while (rNum.indexOf(curChar)!=-1) {
if (rNum.indexOf(curChar)==0) {
if (order2.indexOf(curChar)!=-1) {
aNum+=((int)Math.pow(10, order2.indexOf(curChar)));
}
else if (order3.indexOf(curChar)!=-1) {
aNum+=(5*((int)Math.pow(10, order3.indexOf(curChar))));
}
rNum = rNum.substring(1);
}
else if (rNum.indexOf(curChar)==1) {
if (order2.indexOf(curChar)!=-1) {
aNum+=((int)(Math.pow(10, order2.indexOf(curChar))-Math.pow(10, order2.indexOf(curChar)-1)));
}
else if (order3.indexOf(curChar)!=-1) {
aNum+=((int)((5*Math.pow(10, order3.indexOf(curChar)))-Math.pow(10,order3.indexOf(curChar))));
}
rNum = rNum.substring(2);
}
}
}
return aNum;
}
private static String a2R(int num, String theNum) {
// num is the digit of an Arabic digit number to be replaced by Roman Numerals for that digit
// theNum is the value of Roman Numerals that would go into the specific digit place (tens, ones,...)
String rNum = "";
if (!theNum.equals("M")) {
if (num==9) {
rNum = theNum + order.charAt(order.indexOf(theNum)+2);
}
else if (num==4) {
rNum = theNum + order.charAt(order.indexOf(theNum)+1);
}
else if (num>=5) {
rNum+= order.charAt(order.indexOf(theNum)+1);
for (int i = 0; i < num-5; i++) {
rNum+=theNum;
}
}
else {
for (int i = 0; i < num; i++) {
rNum+=theNum;
}
}
}
else {
for (int i = 0; i < num; i++) {
rNum+=theNum;
}
}
return rNum;
}
}
`
I expect the TLE is being caused by your program never terminating.
Currently you have a while (true) loop, which breaks when you see a blank line.
According to the problem however...
The input consists of several lines, each one containing
either an Arabic or a Roman number n, where 0 < n < 4000.
Nowhere does it state that there will be an extra blank line terminating the input.
So your program will not terminate, forever waiting until an extra blank line has been entered.
Instead of reading your input like this
while (true) {
String read = "";
int aNum = 0;
String rNum = "";
try {
read = br.readLine();
if (read=="")
break;
} catch (IOException e) {
if (read=="")
break;
e.printStackTrace();
}
//etc
try this instead
String read = "";
while ((read = br.readLine()) != null) {
int aNum = 0;
String rNum = "";
//etc
I solved my problem by going about it in a different manner, I used a couple of HashMaps to map Roman numeral values to Arabic numeral values and vice versa. I had four helper methods: one would set up the hashmaps, another would convert from Roman numeral to Arabic numeral, and the other two would work together to convert from Arabic numeral to Roman numeral.
The method that converted from Roman to Arabic would go through the string in a for loop starting from the beginning of the string. It would check if the length of the string was greater than one, and if so it would then check if the substring of the first two values are in the Roman to Arabic hashmap. If so, it would then add the value that the Roman numeral value equates to to an int variable. The method would also check substrings of length 1.
In the methods that converted from Arabic to Roman, the input integer would first be analyzed then it would be torn apart into its little pieces. In the first method, four integer values would first be produced: the thousands value, the hundreds value, the tens value, then the ones value. The second method would organize these values into the correct Roman numeral form.
Thanks to everybody who helped me solve this problem, I did not realize some of the mistakes that I made, probably due to my inexperience in programming so this was a great learning experience for myself.
Below is my solution:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
class Main {
private static HashMap<String,Integer> r2A = new HashMap<String,Integer>();
private static HashMap<Integer,String> a2R = new HashMap<Integer,String>();
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
makeMaps();
String read;
StringBuilder answers = new StringBuilder("");
while ((read=br.readLine())!=null) {
int aNum = 0;
String rNum = "";
try {
aNum = Integer.parseInt(read);
System.out.println(arab2Roman(aNum));
} catch (NumberFormatException c) {
rNum = read;
int ans = roman2Arab(rNum);
System.out.println(ans);
}
}
}
private static int roman2Arab(String rNum) {
int aNum = 0;
for (int i = 0; i < rNum.length(); i++) {
boolean done = false;
String theNum = rNum.substring(i,i+1);
if (i < rNum.length()-1) {
String part = rNum.substring(i, i+2);
if (r2A.containsKey(part)) {
aNum+=r2A.get(part);
i++;
done = true;
}
}
if (!done) {
if (r2A.containsKey(theNum)) {
aNum+=r2A.get(theNum);
}
}
}
return aNum;
}
private static String arab2Roman(int num) {
StringBuilder rNum = new StringBuilder("");
int thousands = num-(num%1000);
int hundreds = ((num/100)%10)*100;
int tens = ((num/10)%10)*10;
int ones = num%10;
rNum.append(simpleConv(thousands,"thousands"));
rNum.append(simpleConv(hundreds,"hundreds"));
rNum.append(simpleConv(tens,"tens"));
rNum.append(simpleConv(ones,"ones"));
return rNum.toString();
}
private static String simpleConv(int num, String place) {
StringBuilder ans = new StringBuilder("");
int pNum = (place.equals("thousands")) ? 1000 : (place.equals("hundreds")) ? 100 : (place.equals("tens")) ? 10 : 1;
if (a2R.containsKey(num)) {
ans.append(a2R.get(num));
}
else {
if (num/pNum>=5) {
ans.append(a2R.get(5*pNum));
for (int i = 0; i < ((num/pNum)-5); i++) {
ans.append(a2R.get(pNum));
}
}
else {
for (int i = 0; i < num/pNum; i++) {
ans.append(a2R.get(pNum));
}
}
}
return ans.toString();
}
private static void makeMaps() {
// First r2A
r2A.put("I", 1);
r2A.put("IV", 4);
r2A.put("V", 5);
r2A.put("IX", 9);
r2A.put("X", 10);
r2A.put("XL", 40);
r2A.put("L", 50);
r2A.put("XC", 90);
r2A.put("C", 100);
r2A.put("CD", 400);
r2A.put("D", 500);
r2A.put("CM", 900);
r2A.put("M", 1000);
// Second a2R
a2R.put(1, "I");
a2R.put(4, "IV");
a2R.put(5, "V");
a2R.put(9, "IX");
a2R.put(10, "X");
a2R.put(40, "XL");
a2R.put(50, "L");
a2R.put(90, "XC");
a2R.put(100, "C");
a2R.put(400, "CD");
a2R.put(500, "D");
a2R.put(900, "CM");
a2R.put(1000, "M");
}
}