Из римского в целое число - но с использованием другой римской системы счисления

У меня было интервью, в котором я ужасно выступил. Итак, сейчас я пытаюсь найти решение вопроса. Вот вопрос для интервью:

"У нас есть следующее сопоставление:
M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1.

И у нас есть следующие правила:

  1. Каждая буква соответствует положительному целочисленному значению

  2. Вы складываете значения вместе, кроме ...

  3. ... когда за значением (или сериями тех же значений) следует большее значение, вы вычитаете сумму этого ряда значений.

Примеры:

IIX -> 8

MCCMIIX -> 1808 г.

Нам дан этот Java-метод: int valueOfRoman(char roman). Мы реализовали метод Java: int romanToInt(String s) "

Я знаю, что это неправильная римская система счисления, но вопрос именно в этом.

Я смог написать рабочее решение для правильной римской системы. Но я не могу изменить его так, чтобы он адаптировался к этим новым правилам, в частности к Правилу 3. Я пробовал, но безуспешно. Каково мое решение прямо сейчас, для IIX оно выводит 10 вместо правильного ответа 8. Вот мой код (я также реализовал valueOf для своего тестирования):

static int romanToInt(String s) {
    char curr;
    int currVal;
    char prev;
    int prevVal;

    int total = valueOfRoman(s.charAt(0));

    for (int i = 1; i < s.length(); i++) {
        curr = s.charAt(i);
        currVal = valueOfRoman(curr);

        prev = s.charAt(i-1);
        prevVal = valueOfRoman(prev);

        total += currVal;
        if(currVal > prevVal) {
            total = total - (2*prevVal);
        }

    }
    return total;
}


static int valueOfRoman(char c) {
    if (c == 'M') {
        return 1000;
    } else if (c == 'D') {
        return 500;
    } else if (c == 'C') {
        return 100;
    } else if (c == 'L') {
        return 50;
    } else if (c == 'X') {
        return 10;
    } else if (c == 'V') {
        return 5;
    } else if (c == 'I') {
        return 1;
    } 

    return -1;
}

Любая помощь действительно приветствуется. Особенно полезно будет, если вы скажете мне, как изменить мой код. Спасибо!

РЕДАКТИРОВАТЬ: Я отредактировал названия методов, чтобы они были более понятными.


person user224567893    schedule 13.03.2015    source источник
comment
Мне кажется рабочее решение, в чем проблема? Есть ли вход, который производит нежелательный результат?   -  person Jacopofar    schedule 13.03.2015
comment
Каково мое решение прямо сейчас, для IIX оно выводит 10 вместо правильного ответа 8.   -  person user224567893    schedule 13.03.2015
comment
Вы, кажется, предполагаете, что каждый прогон длится 2 длинных, что не гарантируется. Вы должны следить за продолжительностью текущего набора одинаковых цифр.   -  person biziclop    schedule 13.03.2015
comment
@biziclop Число может быть IIIIX, и ответ должен быть 6   -  person user224567893    schedule 13.03.2015
comment
Какую ценность должен производить IVX?   -  person OldCurmudgeon    schedule 13.03.2015
comment
Подсказка: начните справа.   -  person Fildor    schedule 13.03.2015
comment
Пожалуйста, еще проработанные примеры. Если вы не можете решить проблему на бумаге, значит, вы не можете решить ее с помощью компьютера.   -  person Colonel Panic    schedule 16.03.2015


Ответы (6)


Мое мнение - работает с небольшими тестами, которые вы предоставили.

static int rom2int(String s) {
    if (s == null || s.length() == 0) {
        return 0;
    }
    // Total value.
    int total = 0;
    // The most recent.
    char current = s.charAt(0);
    // Total for the current run.
    int run = valueOf(current);

    for (int i = 1; i < s.length(); i++) {
        char next = s.charAt(i);
        int value = valueOf(next);
        if (next == current) {
            // We're in a run - just keep track of its value.
            run += value;
        } else {
            // Up or down?
            if (value < valueOf(current)) {
                // Gone down! Add.
                total += run;
            } else {
                // Gone UP! Subtract.
                total -= run;
            }
            // Run ended.
            run = valueOf(next);
        }
        // Kee track of most recent.
        current = next;
    }
    return total + run;
}

private void test(String s) {
    System.out.println("Value of " + s + " = " + rom2int(s));
}

public void test() {
    test("IVX");
    test("IIVVL");
    test("IIX");
    test("MCCMIIX");
    test("MVVV");
}

отпечатки

Value of IVX = 4 - Odd!!!
Value of IIVVL = 38
Value of IIX = 8
Value of MCCMIIX = 1808
Value of MVVV = 1015
person OldCurmudgeon    schedule 13.03.2015
comment
@ user224567893 - На самом деле немного странно - он интерпретирует это так - он видит IV как (-1 + ...) и VX как (-5 + ...), что дает 10-1-5 = 4? ... когда за значением (или сериями тех же значений) следует большее значение, вы вычитаете общую сумму этой серии значений. - person OldCurmudgeon; 13.03.2015
comment
Я думал, что пробежки одних и тех же ценностей были на одном и том же. Но я начинаю сомневаться - person user224567893; 13.03.2015
comment
@ user224567893 - Я попросил у OP разъяснений. - person OldCurmudgeon; 13.03.2015
comment
@OldCurmudgeon Я прочитал это как то же самое в случае IVX (V-I) + (X - V) - person gtgaxiola; 13.03.2015
comment
@OldCurmudgeon Я ОП. Интервьюер не упомянул этот пример, поэтому я не уверен - person user224567893; 13.03.2015
comment
@ user224567893 - Ой! Хорошо, тогда ваш звонок - мой код - идеальная реализация ваших правил - несмотря на неожиданный результат IVX, или я обречен на -1 за странность? :) - person OldCurmudgeon; 13.03.2015
comment
@ user224567893 - А как насчет IIVVL. Я получаю 38, что означает -2 + -10 + 50 = 38. - person OldCurmudgeon; 13.03.2015

Вот как я подхожу к проблеме:

  • Read the string character by character and during every step note the current character and the previous character.
    • If the current character is the same as the previous, increase the run length by 1.
    • Если текущий символ имеет меньшее значение, чем предыдущий, добавьте длину серии * значение предыдущего символа к общему значению и сбросьте длина пробега до 1.
    • Если текущий символ имеет большее значение, чем предыдущий, вычтите длину серии * значение предыдущего символа из общего и сбросьте длина пробега до 1.
person biziclop    schedule 13.03.2015
comment
Как следует использовать IVX? Как (10-5) -1 или как 1+ (10-5)? - person Jacopofar; 13.03.2015
comment
@Jac_opo Это хороший вопрос, но согласно правилам, указанным OP, IVX должен быть (5-1) + (10-5) = 9 - person biziclop; 13.03.2015
comment
когда за значением (или сериями тех же значений) следует большее значение. Итак, для IVX ответ должен быть (5-1) + (10-5) - person user224567893; 13.03.2015
comment
Что произойдет, если вы получите поток равных чисел? Пример: MVVV в вашем подходе нет добавления, когда current character is the same as the previous - person gtgaxiola; 13.03.2015
comment
@gigaxiola Да, я остановился на стартовом и финишном ходах, чтобы сконцентрироваться на логике основного цикла. Но на самом деле это не так уж и сложно, вы начинаете со всем, установленным на ноль, а любые остатки в конце вы добавляете к общей сумме. Я сознательно не хотел публиковать полное решение. - person biziclop; 13.03.2015

Так что никто не уловил моего намека. Тогда я тоже попробую. Я не буду вдаваться в подробности «IVX», потому что считаю это синтаксической ошибкой.

int romanToInt( String s ){
    int total = 0;
    int pivot = 0;

    for( int i = s.length()-1; i >= 0; i--){ // We start at the **end**
        int current = valueOfRoman((s.charAt(i));
        if( current >= pivot ){ // We will have at least "I", so it **will** be > pivot for 1st char.
            pivot = current; 
            total += pivot;
        }else{
            total -= current;
        }
    }
    return total;
}

Посмотрим: IIX

i    char    value    total    pivot   ->   total   pivot
2     X        10       0        0      >      10      10
1     I         1      10       10      <       9      10
0     I         1       9       10      <       8      10

MCCMIIX

i    char    value    total    pivot   ->   total   pivot
6     X        10       0        0     >       10      10
5     I         1      10       10     <        9      10
4     I         1       9       10     <        8      10
3     M      1000       8       10     >     1008    1000
2     C       100    1008     1000     <      908    1000
1     C       100     908     1000     <      808    1000
0     M      1000     808     1000     =     1808    1000

Для краткости метод не включает проверку ввода. Я предполагаю, что весь ввод был проверен и состоит только из разрешенных символов в соответствии с «правилами».

person Fildor    schedule 13.03.2015
comment
Чем это отличается от того, что я опубликовал? Помимо того, что вы делаете это задом наперед и храните постоянно обновляемую предварительную сумму, а не длину прогона? - person biziclop; 16.03.2015
comment
Точно. Вы назвали разницу. В моем решении не нужно отслеживать длину пробега. Лично я считаю это наиболее читаемым и простым. @biziclop - person Fildor; 16.03.2015
comment
Я не знаю, но достаточно справедливо. :) - person biziclop; 16.03.2015

Мой взгляд на это.

ИЗМЕНИТЬ ИЗМЕНЕНИЕ №2

public class Romans {

private int valueOf(char c) {
    if (c == 'M') {
        return 1000;
    } else if (c == 'D') {
        return 500;
    } else if (c == 'C') {
        return 100;
    } else if (c == 'L') {
        return 50;
    } else if (c == 'X') {
        return 10;
    } else if (c == 'V') {
        return 5;
    } else if (c == 'I') {
        return 1;
    }

    return 0;
}

public int rom2int(String s) {
    int currVal;
    int runValue = 0;
    int repetition = 0;
    int total = 0;
    boolean alreadyAdded = false;
    for (int i = 0; i < s.length(); i++) {
        currVal = valueOf(s.charAt(i));
        if (runValue == 0) {
            runValue = currVal;
            repetition = 1;
            alreadyAdded = false;
        } else if (currVal > runValue) {
            total = total + (currVal - (runValue * repetition));
            repetition = 1;
            runValue = currVal;
            alreadyAdded = true;
        } else if (currVal < runValue) {
            if(!alreadyAdded) {
                total += (runValue * repetition);
            }
            repetition = 1;
            runValue = currVal;
            alreadyAdded = false;
        } else {
            repetition++;
            alreadyAdded = false;
        }
    }

    if (!alreadyAdded) {
        total += (runValue * repetition);
    }

    return total;
}

}

И основное, что я запускаю:

public static void main(String[] args) {
    Romans r = new Romans();
    String[] inputs =  {"MVVV", "IIX","MCCMIIX", "IVX"};
    for(String input : inputs) {
        System.out.println("Value of " + input + " is: " + r.rom2int(input));
    }
}

Выходы:

Value of MVVV is: 1015
Value of IIX is: 8
Value of MCCMIIX is: 1808
Value of IVX is: 9
person gtgaxiola    schedule 13.03.2015
comment
@Jean третий вход да. (Удаляем до исправления) - person gtgaxiola; 13.03.2015
comment
@ Jean-FrançoisSavard Последнее верно, если вы внимательно прочитаете правила. - person biziclop; 13.03.2015
comment
Каким должен быть правильный вывод IVX? Я начинаю сомневаться в себе. - person user224567893; 13.03.2015
comment
@ user224567893 Я думаю, что понял это .. И я считаю, что ответ для IVX должен быть 9 - person gtgaxiola; 13.03.2015

Так я и поступил.

Он работает для тех двух значений, которые вы упомянули (IIX = 8 и MCCMIIX = 1808):

public static int rom2int(String s)
{
    int currVal = 0, prevVal = 0, subTotal = 0, total = 0;
    for (int i = 0; i < s.length(); i++) {
        currVal = valueOf(s.charAt(i));
        if (currVal > 0) {
            if (prevVal == 0) {
                subTotal = currVal;
            }
            else if (currVal > prevVal) {
                total += (currVal - subTotal);
                subTotal = 0;
            }
            else if (currVal < prevVal) {
                total += subTotal;
                subTotal = currVal;
            }
            else if (currVal == prevVal) {
                subTotal += currVal;
            }
            prevVal = currVal;
        }
    }
    total += subTotal;
    return total;
}

public static int valueOf(char c)
{
    if (c == 'M')
        return 1000;
    if (c == 'D')
        return 500;
    if (c == 'C')
        return 100;
    if (c == 'L')
        return 50;
    if (c == 'X')
        return 10;
    if (c == 'V')
        return 5;
    if (c == 'I')
        return 1;
    return 0;
}

ИЗМЕНИТЬ 1:

Пояснение к значению "IVX":

«... сложите значения вместе, за исключением случаев, когда за значением (или сериями ОДИНАКОВЫХ значений) следует большее значение, вы вычитаете сумму этого ряда значений».

 IVX = 1 5 10
 if  5 > 1 then   5 - 1    = 4
 if 10 > 5 then  10 - 0(*) = 10 (*) we already have used V(5) before, so we discard it.

Итак, ответ для IVX - 14!

person Christian    schedule 13.03.2015
comment
14. Вы этого ожидаете? - person Christian; 13.03.2015
comment
Нет упоминания об отказе от ранее использованных значений - person gtgaxiola; 13.03.2015
comment
Это явное слово EXCEPT, или нет? - person Christian; 13.03.2015
comment
сложите значения вместе except, когда ..... подразумевает часть ДОБАВЛЕНИЕ, а не часть ПОДСТРОЙКА .. В любом случае инструкции неоднозначны, так что ваш ответ правильный, а мой - правильный;) - person gtgaxiola; 13.03.2015
comment
Так что это не проблема алгоритма. Это проблема интерпретации текста! - person Christian; 13.03.2015
comment
@Christian напоминает мне: «Купи молоко в супермаркете, а если увидишь, купи 12 яиц…» Этот парень приносит 12 пакетов молока. - person gtgaxiola; 13.03.2015
comment
И это неправильно? Оба ответа верны, не правда ли? - person Christian; 13.03.2015
comment
Начать следует с: Шредингер идет в супермаркет ... - person gtgaxiola; 13.03.2015

такого рода проблемы обычно легко решить, используя рекурсивный способ мышления. Решение могло бы выглядеть так:

public int rom2int(String s)
{               
    if(s.length() == 0)   
        // no string --> 0
        return  0;
    else if(s.length() == 1)
        // One Character --> Value of Character
        return valueOf(s.charAt(0)); 
    else if((valueOf(s.charAt(0)) > valueOf(s.charAt(1))) ) 
        // The value is NOT followed by a greater value --> We had the value
        return rom2int(s.substring(1, s.length())) + valueOf(s.charAt(0));      
    else if(valueOf(s.charAt(0)) <= valueOf(s.charAt(1)) )
        // The value is followed by a greater (or same) value --> We substract the value
        return rom2int(s.substring(1, s.length())) - valueOf(s.charAt(0));
    else
        // Shouldn't Happen. 0 as neutral element in in a Sum.
        return 0;
}

Даже если рекурсивное решение запрещено, на мой взгляд, проще сделать этот алгоритм нерекурсивным, чем найти процедурный с первого раза =)

Изменить: МОИ Результаты:

Значение MCCMIIX: 1808

Значение IIX: 8

Значение IVX: 4

Значение IIVVL составляет: 38

person A. Ocannaille    schedule 13.03.2015
comment
1. string не является допустимым объектом. 2. Вам не хватает пары скобок. 3. Если ни одно условие не соответствует, ничего не будет возвращено, следовательно, он не будет компилироваться. - person Jean-François Savard; 13.03.2015
comment
4. Логика неправильная, протестировал ее и провалил первый вариант использования (IIX) даже после исправления ошибок компиляции. - person Jean-François Savard; 13.03.2015
comment
Это как бы упоминание могло бы выглядеть как предложение алгоритма. Но я согласен, что это не идеально. Я изменил несколько деталей, все еще как алгоритм, так как на этом устройстве у меня нет среды разработки. Дело в том, может ли рекурсивный способ решить эту проблему? Думаю (после нескольких улучшений). Кто-нибудь предлагал это раньше? Нет. Это проще? да. - person A. Ocannaille; 13.03.2015
comment
Сделанный. Если вы можете его скомпилировать и протестировать, я бы хотел проверить, действителен ли мой слепой код :) - person A. Ocannaille; 13.03.2015
comment
Уже сделал: P И жаль, что это не так. Однако кажется, что OP даже больше не уверен, каков желаемый результат, поэтому трудно что-то исправить. - person Jean-François Savard; 13.03.2015
comment
Преимущество рекурсивного способа состоит в том, что каждый возврат является прямым переводом правила. Если мои условия верны и мой перевод верен, вывод правильный, никаких побочных эффектов. Что недействительно? - person A. Ocannaille; 13.03.2015
comment
После тестирования; мой код компилируется и возвращает хороший результат для 2 опубликованных чисел (и я обновил результат для других значений). Почему у меня -2? Пугает ли рецидивистов избирателей? - person A. Ocannaille; 16.03.2015
comment
Я действительно хочу знать, почему мне отказали, поскольку мое решение дает в точности тот же результат, что и решения Fildor One и OldCurmudgeon. Каким образом мое решение может быть оценено как нерелевантное, неэлегантное или что-то еще, за что стоит проголосовать против? - person A. Ocannaille; 18.03.2015